init webseite ts and claude
This commit is contained in:
@@ -509,7 +509,7 @@ Projekte + weiteres Modul einbinden
|
|||||||
|
|
||||||
**Übungen:**
|
**Übungen:**
|
||||||
|
|
||||||
Übungen 10 - 18
|
Übungen 10 - 19
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -519,11 +519,10 @@ Projekte + weiteres Modul einbinden
|
|||||||
|
|
||||||
- TS Funktionen und Objekte
|
- TS Funktionen und Objekte
|
||||||
- Abschluss TypeScript Grundlagen
|
- Abschluss TypeScript Grundlagen
|
||||||
- Abschlussquiz
|
|
||||||
|
|
||||||
**Übungen:**
|
**Übungen:**
|
||||||
|
|
||||||
Übungen 19 - 31
|
Übungen 20 - 31
|
||||||
Projektarbeit
|
Projektarbeit
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -532,6 +531,7 @@ Projektarbeit
|
|||||||
|
|
||||||
**Inhalt:**
|
**Inhalt:**
|
||||||
|
|
||||||
|
- Abschlussquiz
|
||||||
- Projekt-Webseite in TS
|
- Projekt-Webseite in TS
|
||||||
- Optimierung mit KI
|
- Optimierung mit KI
|
||||||
- 1:1 Meeting
|
- 1:1 Meeting
|
||||||
|
|||||||
10
04_typescript/faq/describable-fn.js
Normal file
10
04_typescript/faq/describable-fn.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const greet = () => {
|
||||||
|
return 'Hello!';
|
||||||
|
};
|
||||||
|
|
||||||
|
greet.description = 'Function that says hello';
|
||||||
|
greet.anotherProp = 'Wert';
|
||||||
|
|
||||||
|
console.log(greet());
|
||||||
|
console.log(greet.description);
|
||||||
|
console.log(greet.anotherProp);
|
||||||
29
04_typescript/faq/describable-fn.ts
Normal file
29
04_typescript/faq/describable-fn.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Type - describable Function
|
||||||
|
type GreetFn = {
|
||||||
|
anotherProp: string;
|
||||||
|
(): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Type - Object
|
||||||
|
type Greet = {
|
||||||
|
prop: string;
|
||||||
|
greed: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const greetObj: Greet = {
|
||||||
|
prop: 'bin ein objekt',
|
||||||
|
greed: () => {
|
||||||
|
console.log('hello');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const greet: GreetFn = () => {
|
||||||
|
return 'Hello!';
|
||||||
|
};
|
||||||
|
|
||||||
|
// greet.description = 'Function that says hello';
|
||||||
|
greet.anotherProp = 'Wert';
|
||||||
|
|
||||||
|
console.log(greet());
|
||||||
|
// console.log(greet.description);
|
||||||
|
console.log(greet.anotherProp);
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
(() => {
|
||||||
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||||
|
var __commonJS = (cb, mod) => function __require() {
|
||||||
|
try {
|
||||||
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||||
|
} catch (e) {
|
||||||
|
throw mod = 0, e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// src/main.ts
|
||||||
|
var require_main = __commonJS({
|
||||||
|
"src/main.ts"() {
|
||||||
|
(() => {
|
||||||
|
const double = (num) => num * 2;
|
||||||
|
const halve = (num) => num / 2;
|
||||||
|
const describeAndApply = (dnObj, num) => {
|
||||||
|
console.log(dnObj.description);
|
||||||
|
return dnObj(num);
|
||||||
|
};
|
||||||
|
const describableDouble = Object.assign(double, {
|
||||||
|
description: "This operation doubles the number."
|
||||||
|
});
|
||||||
|
const describableHalve = Object.assign(halve, {
|
||||||
|
description: "This operation halves the number."
|
||||||
|
});
|
||||||
|
console.log(describeAndApply(describableDouble, 10));
|
||||||
|
console.log(describeAndApply(describableHalve, 10));
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
require_main();
|
||||||
|
})();
|
||||||
@@ -1,12 +1,35 @@
|
|||||||
// Step 1: Define the NumberOperation call signature
|
(() => {
|
||||||
|
// Übung 20: Call Signatures für Zahlenoperationen
|
||||||
|
|
||||||
// Step 2: Implement double and halve functions
|
// In dieser Übung erstellst und verwendest du ein paar einfache Funktionen, die sich an bestimmte Call Signatures für die Arbeit mit Zahlen halten.
|
||||||
|
|
||||||
// Step 3: Define the DescribableNumberOperation type
|
// Anweisungen:
|
||||||
|
|
||||||
// Step 4: Implement describeAndApply function
|
// 1 Definiere eine Funktion vom Typ NumberOperation, die ein einzelnes number annimmt und ein number zurückgibt.
|
||||||
|
|
||||||
// Test all functions
|
type NumberOperation = (num: number) => number;
|
||||||
|
|
||||||
|
// 2 Implementiere zwei Funktionen, double und halve, die der Call Signature NumberOperation folgen:
|
||||||
|
|
||||||
|
// double: Multipliziert die eingegebene Zahl mit 2.
|
||||||
|
const double: NumberOperation = (num) => num * 2; // keine geschweiften Klammern
|
||||||
|
|
||||||
|
// halve: Teilt die eingegebene Zahl durch 2.
|
||||||
|
const halve: NumberOperation = (num) => num / 2;
|
||||||
|
|
||||||
|
// 3 Definiere einen Objekttyp DescribableNumberOperation mit einer description-Property vom Typ string und einer Cal Signature, die NumberOperation entspricht.
|
||||||
|
type DescribableNumberOperation = {
|
||||||
|
description: string;
|
||||||
|
(num: number): number;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4 Implementiere eine Funktion describeAndApply, die ein DescribableNumberOperation Objekt annimmt, dessen description protokolliert und die Operation auf eine bestimmte Zahl anwendet.
|
||||||
|
const describeAndApply = (dnObj: DescribableNumberOperation, num: number) => {
|
||||||
|
console.log(dnObj.description);
|
||||||
|
return dnObj(num);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 5 Teste alle Funktionen mit ein paar Zahlen.
|
||||||
const describableDouble: DescribableNumberOperation = Object.assign(double, {
|
const describableDouble: DescribableNumberOperation = Object.assign(double, {
|
||||||
description: 'This operation doubles the number.',
|
description: 'This operation doubles the number.',
|
||||||
});
|
});
|
||||||
@@ -17,22 +40,4 @@ const describableHalve: DescribableNumberOperation = Object.assign(halve, {
|
|||||||
|
|
||||||
console.log(describeAndApply(describableDouble, 10)); // => 20
|
console.log(describeAndApply(describableDouble, 10)); // => 20
|
||||||
console.log(describeAndApply(describableHalve, 10)); // => 5
|
console.log(describeAndApply(describableHalve, 10)); // => 5
|
||||||
|
})();
|
||||||
// Übung 20: Call Signatures für Zahlenoperationen
|
|
||||||
|
|
||||||
// In dieser Übung erstellst und verwendest du ein paar einfache Funktionen, die sich an bestimmte Call Signatures für die Arbeit mit Zahlen halten.
|
|
||||||
|
|
||||||
// Anweisungen:
|
|
||||||
|
|
||||||
// 1 Definiere eine Funktion vom Typ NumberOperation, die ein einzelnes number annimmt und ein number zurückgibt.
|
|
||||||
|
|
||||||
// 2 Implementiere zwei Funktionen, double und halve, die der Call Signature NumberOperation folgen:
|
|
||||||
|
|
||||||
// double: Multipliziert die eingegebene Zahl mit 2.
|
|
||||||
// halve: Teilt die eingegebene Zahl durch 2.
|
|
||||||
|
|
||||||
// 3 Definiere einen Objekttyp DescribableNumberOperation mit einer description-Property vom Typ string und einer Cal Signature, die NumberOperation entspricht.
|
|
||||||
|
|
||||||
// 4 Implementiere eine Funktion describeAndApply, die ein DescribableNumberOperation Objekt annimmt, dessen description protokolliert und die Operation auf eine bestimmte Zahl anwendet.
|
|
||||||
|
|
||||||
// 5 Teste alle Funktionen mit ein paar Zahlen.
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
(() => {
|
||||||
// Übung 21: Eine allgemeine Identitätsfunktion erstellen
|
// Übung 21: Eine allgemeine Identitätsfunktion erstellen
|
||||||
|
|
||||||
// In dieser Übung erstellst du eine einfache generische Funktion namens identity, die den ihr übergebenen Wert zurückgibt. So lernst du, wie generische Funktionen mit verschiedenen Typen arbeiten können, ohne die Typsicherheit zu verlieren.
|
// In dieser Übung erstellst du eine einfache generische Funktion namens identity, die den ihr übergebenen Wert zurückgibt. So lernst du, wie generische Funktionen mit verschiedenen Typen arbeiten können, ohne die Typsicherheit zu verlieren.
|
||||||
@@ -6,10 +7,27 @@
|
|||||||
|
|
||||||
// 1 Implementiere eine generische identity Funktion, die einen Parameter vom Typ T annimmt und denselben Wert zurückgibt.
|
// 1 Implementiere eine generische identity Funktion, die einen Parameter vom Typ T annimmt und denselben Wert zurückgibt.
|
||||||
|
|
||||||
|
const identity = <T>(x: T): T => {
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
// 2 Rufe identity mit einer number auf und speichere das Ergebnis.
|
// 2 Rufe identity mit einer number auf und speichere das Ergebnis.
|
||||||
|
|
||||||
|
const resultNum = identity<number>(2);
|
||||||
|
|
||||||
// 3 Rufe identity mit einer string auf und speichere das Ergebnis.
|
// 3 Rufe identity mit einer string auf und speichere das Ergebnis.
|
||||||
|
|
||||||
|
const resultString = identity<string>('John');
|
||||||
|
|
||||||
// 4 Rufe identity mit einem Array von Objekten auf und speichere das Ergebnis.
|
// 4 Rufe identity mit einem Array von Objekten auf und speichere das Ergebnis.
|
||||||
|
|
||||||
|
const resultArr = identity<object[]>([
|
||||||
|
{ firstName: 'John' },
|
||||||
|
{ lastName: 'Wick' },
|
||||||
|
{ firstName: 'Jane', lastName: 'Doe' },
|
||||||
|
]);
|
||||||
|
|
||||||
// 5 Stelle sicher, dass TypeScript die Rückgabetypen ohne explicit Type Annotations korrekt herleitet.
|
// 5 Stelle sicher, dass TypeScript die Rückgabetypen ohne explicit Type Annotations korrekt herleitet.
|
||||||
|
|
||||||
|
console.log(`Num: ${resultNum}, String: ${resultString}, Arr: ${JSON.stringify(resultArr)}`);
|
||||||
|
})();
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
createUserProfile('JohnDoe', 25, 'john@example.com');
|
(() => {
|
||||||
// => "User JohnDoe is 25 years old and can be contacted at john@example.com."
|
|
||||||
|
|
||||||
createUserProfile('JaneDoe');
|
|
||||||
// => "User JaneDoe is 18 years old and can be contacted at N/A."
|
|
||||||
|
|
||||||
// Übung 22: Optionale Parameter implementieren
|
// Übung 22: Optionale Parameter implementieren
|
||||||
|
|
||||||
// Erstelle die Funktion createUserProfile, die drei Parameter benötigt: einen obligatorischen username und zwei optionale Parameter, age und email. Die Funktion sollte einen String mit einer Profilzusammenfassung zurückgeben. Wenn keine optionalen Parameter angegeben werden, sollten die Standardwerte verwendet werden, um das Profil zu vervollständigen.
|
// Erstelle die Funktion createUserProfile, die drei Parameter benötigt: einen obligatorischen username und zwei optionale Parameter, age und email. Die Funktion sollte einen String mit einer Profilzusammenfassung zurückgeben. Wenn keine optionalen Parameter angegeben werden, sollten die Standardwerte verwendet werden, um das Profil zu vervollständigen.
|
||||||
@@ -16,4 +11,16 @@ createUserProfile('JaneDoe');
|
|||||||
// age (optional, Zahl, Standardwert ist 18)
|
// age (optional, Zahl, Standardwert ist 18)
|
||||||
// email (optional, String, Standardwert: "N/A")
|
// email (optional, String, Standardwert: "N/A")
|
||||||
|
|
||||||
|
// Keine Fragezeichen nötig
|
||||||
|
const createUserProfile = (username: string, age: number = 18, email: string = 'N/A') => {
|
||||||
|
return `User ${username} is ${age} years old and can be contacted at ${email}.`;
|
||||||
|
};
|
||||||
|
|
||||||
// Die Funktion sollte einen String zurückgeben, der das Profil des Nutzers zusammenfasst.
|
// Die Funktion sollte einen String zurückgeben, der das Profil des Nutzers zusammenfasst.
|
||||||
|
|
||||||
|
console.log(createUserProfile('JohnDoe', 25, 'john@example.com'));
|
||||||
|
// => "User JohnDoe is 25 years old and can be contacted at john@example.com."
|
||||||
|
|
||||||
|
console.log(createUserProfile('JaneDoe'));
|
||||||
|
// => "User JaneDoe is 18 years old and can be contacted at N/A."
|
||||||
|
})();
|
||||||
|
|||||||
@@ -1,15 +1,4 @@
|
|||||||
// Example usage of sumAll
|
(() => {
|
||||||
// Define total
|
|
||||||
console.log(total); // => 15
|
|
||||||
|
|
||||||
// Example usage of calculateHypotenuse with spread syntax
|
|
||||||
// Define hypotenuse
|
|
||||||
console.log(hypotenuse); // => 5
|
|
||||||
|
|
||||||
// Attempting to call calculateHypotenuse with incorrect number of arguments
|
|
||||||
const invalidSides = [3, 4, 5];
|
|
||||||
// Define invalidHypotenuse to call calculateHypotenuse with invalidSides => TypeScript Error: Expected 2 arguments, but got 3.
|
|
||||||
|
|
||||||
// Übung 23: Restparameter und Restargumente
|
// Übung 23: Restparameter und Restargumente
|
||||||
|
|
||||||
// Schreibe zwei Funktionen: eine, die Restparameter verwendet, um eine unterschiedliche Anzahl von Zahlen zu akzeptieren und ihre Summe zu berechnen, und eine andere, die die Spread-Syntax verwendet, um ein Array von Zahlen in einzelne Argumente für eine mathematische Funktion zu verteilen.
|
// Schreibe zwei Funktionen: eine, die Restparameter verwendet, um eine unterschiedliche Anzahl von Zahlen zu akzeptieren und ihre Summe zu berechnen, und eine andere, die die Spread-Syntax verwendet, um ein Array von Zahlen in einzelne Argumente für eine mathematische Funktion zu verteilen.
|
||||||
@@ -18,6 +7,36 @@ const invalidSides = [3, 4, 5];
|
|||||||
|
|
||||||
// 1 Definiere eine Funktion sumAll, die einen Restparameter, bestehend aus einer variablen Anzahl von numerischen Argumenten, annimmt und die Summe aller übergebenen Zahlen zurückgibt. Verwende die Methode reduce, um die Summe des Arrays zu berechnen.
|
// 1 Definiere eine Funktion sumAll, die einen Restparameter, bestehend aus einer variablen Anzahl von numerischen Argumenten, annimmt und die Summe aller übergebenen Zahlen zurückgibt. Verwende die Methode reduce, um die Summe des Arrays zu berechnen.
|
||||||
|
|
||||||
|
const sumAll = (...nums: number[]) => {
|
||||||
|
return nums.reduce((sum, num) => sum + num, 0);
|
||||||
|
};
|
||||||
|
|
||||||
// 2 Als Nächstes definierst du eine Funktion namens calculateHypotenuse, die zwei Zahlen als Argumente akzeptiert und die Hypotenuse mithilfe der Formel √(a² + b²) berechnet. Du solltest diese Funktion aufrufen, indem du ein Array mit zwei Zahlen mit Hilfe der Spread-Syntax ausbreitest. (Tipp: Verwende Math.sqrt())
|
// 2 Als Nächstes definierst du eine Funktion namens calculateHypotenuse, die zwei Zahlen als Argumente akzeptiert und die Hypotenuse mithilfe der Formel √(a² + b²) berechnet. Du solltest diese Funktion aufrufen, indem du ein Array mit zwei Zahlen mit Hilfe der Spread-Syntax ausbreitest. (Tipp: Verwende Math.sqrt())
|
||||||
|
const calculateHypotenuse = (...[n1, n2]: [number, number]): number => {
|
||||||
|
return Math.sqrt(n1 ** 2 + n2 ** 2);
|
||||||
|
};
|
||||||
|
|
||||||
// 3 Teste schließlich die Funktion sumAll, indem du sie mit mehreren Zahlen aufrufst und das Ergebnis protokollierst. Teste die Funktion calculateHypotenuse, indem du ein Array mit zwei Zahlen erstellst, dieses Array in die Funktion überträgst und das Ergebnis protokollierst. Versuche außerdem, calculateHypotenuse mit einem Array aufzurufen, das nicht genau zwei Zahlen enthält, um das Type-Checking von TypeScript zu beobachten.
|
// 3 Teste schließlich die Funktion sumAll, indem du sie mit mehreren Zahlen aufrufst und das Ergebnis protokollierst. Teste die Funktion calculateHypotenuse, indem du ein Array mit zwei Zahlen erstellst, dieses Array in die Funktion überträgst und das Ergebnis protokollierst. Versuche außerdem, calculateHypotenuse mit einem Array aufzurufen, das nicht genau zwei Zahlen enthält, um das Type-Checking von TypeScript zu beobachten.
|
||||||
|
|
||||||
|
// console.log(sumAll([1, 2, 3, 4, 5]));
|
||||||
|
// console.log(sumAll(1, 2, 3, 4, 5));
|
||||||
|
|
||||||
|
// Example usage of sumAll
|
||||||
|
// Define total
|
||||||
|
const sumNums = sumAll(1, 2, 3, 4, 5);
|
||||||
|
console.log(sumNums); // => 15
|
||||||
|
console.log(sumAll()); // => 0
|
||||||
|
|
||||||
|
// Example usage of calculateHypotenuse with spread syntax
|
||||||
|
// Define hypotenuse
|
||||||
|
|
||||||
|
console.log(calculateHypotenuse(3, 4)); // => 5
|
||||||
|
|
||||||
|
// Attempting to call calculateHypotenuse with incorrect number of arguments
|
||||||
|
const invalidSides: [number, number] = [3, 4];
|
||||||
|
const invalidSides2: number[] = [3, 4, 5];
|
||||||
|
|
||||||
|
// Define invalidHypotenuse to call calculateHypotenuse with invalidSides => TypeScript Error: Expected 2 arguments, but got 3.
|
||||||
|
calculateHypotenuse(...invalidSides);
|
||||||
|
// calculateHypotenuse(...invalidSides2); // A spread argument must either have a tuple type or be passed to a rest parameter.
|
||||||
|
})();
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
// Example usage of displayUserInfo
|
(() => {
|
||||||
displayUserInfo({ name: 'John' }); // => Hello, John! You are 25 years old.
|
|
||||||
displayUserInfo({ name: 'Alice', age: 30 }); // => Hello, Alice! You are 30 years old.
|
|
||||||
|
|
||||||
// Example usage of getFirstTwoElements
|
|
||||||
getFirstTwoElements([5, 10]); // => First: 5, Second: 10
|
|
||||||
getFirstTwoElements([8]); // => First: 8, Second: undefined
|
|
||||||
|
|
||||||
// Übung 24: Parameter Destrukturierung
|
// Übung 24: Parameter Destrukturierung
|
||||||
|
|
||||||
// In dieser Übung übst du die Anwendung der Parameterdestrukturierung in TypeScript, indem du zwei einfache Funktionen erstellst. Die erste Funktion destrukturiert einen Objektparameter und verwendet einen Standardwert für eine seiner Eigenschaften. Die zweite Funktion destrukturiert einen Array-Parameter. Beide Funktionen enthalten Type Annotations, um sicherzustellen, dass sie die richtige Struktur erhalten.
|
// In dieser Übung übst du die Anwendung der Parameterdestrukturierung in TypeScript, indem du zwei einfache Funktionen erstellst. Die erste Funktion destrukturiert einen Objektparameter und verwendet einen Standardwert für eine seiner Eigenschaften. Die zweite Funktion destrukturiert einen Array-Parameter. Beide Funktionen enthalten Type Annotations, um sicherzustellen, dass sie die richtige Struktur erhalten.
|
||||||
@@ -14,6 +7,30 @@ getFirstTwoElements([8]); // => First: 8, Second: undefined
|
|||||||
|
|
||||||
// 1 Erstelle eine Funktion displayUserInfo, die die Eigenschaften name und age aus einem Objektparameter destrukturiert und einen Standardwert für age liefert.
|
// 1 Erstelle eine Funktion displayUserInfo, die die Eigenschaften name und age aus einem Objektparameter destrukturiert und einen Standardwert für age liefert.
|
||||||
|
|
||||||
|
// interface User {
|
||||||
|
type User = {
|
||||||
|
name: string;
|
||||||
|
age?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayUserInfo = (user: User): void => {
|
||||||
|
const { name, age = 18 } = user;
|
||||||
|
|
||||||
|
console.log(`Hello, ${name}! You are ${age} years old`);
|
||||||
|
};
|
||||||
|
|
||||||
// 2 Erstelle eine Funktion getFirstTwoElements, die die ersten beiden Elemente aus einem Array-Parameter zerstört.
|
// 2 Erstelle eine Funktion getFirstTwoElements, die die ersten beiden Elemente aus einem Array-Parameter zerstört.
|
||||||
|
const getFirstTwoElements = ([firstEl, secondEL]: [number, number?]): void => {
|
||||||
|
console.log(`First: ${firstEl}, Second: ${secondEL}`);
|
||||||
|
};
|
||||||
|
|
||||||
// 3 Teste deine Funktionen, indem du sie mit den richtigen Argumenten aufrufst.
|
// 3 Teste deine Funktionen, indem du sie mit den richtigen Argumenten aufrufst.
|
||||||
|
|
||||||
|
// Example usage of displayUserInfo
|
||||||
|
displayUserInfo({ name: 'John' }); // => Hello, John! You are 25 years old.
|
||||||
|
displayUserInfo({ name: 'Alice', age: 30 }); // => Hello, Alice! You are 30 years old.
|
||||||
|
|
||||||
|
// Example usage of getFirstTwoElements
|
||||||
|
getFirstTwoElements([5, 10]); // => First: 5, Second: 10
|
||||||
|
getFirstTwoElements([8]); // => First: 8, Second: undefined
|
||||||
|
})();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
(() => {
|
||||||
// Übung 25: Optionale und readonly Eigenschaften
|
// Übung 25: Optionale und readonly Eigenschaften
|
||||||
|
|
||||||
// In dieser Übung erstellst du eine interface für eine Product, die erforderliche, optionale und readonly Eigenschaften enthält. Anschließend schreibst du eine Funktion, die Informationen über das Produkt protokolliert und dabei optionale Eigenschaften korrekt behandelt.
|
// In dieser Übung erstellst du eine interface für eine Product, die erforderliche, optionale und readonly Eigenschaften enthält. Anschließend schreibst du eine Funktion, die Informationen über das Produkt protokolliert und dabei optionale Eigenschaften korrekt behandelt.
|
||||||
@@ -10,8 +11,59 @@
|
|||||||
// Optionale Eigenschaften price (Number) und description (String).
|
// Optionale Eigenschaften price (Number) und description (String).
|
||||||
// Eine readonly category (String) Eigenschaft.
|
// Eine readonly category (String) Eigenschaft.
|
||||||
|
|
||||||
|
interface Product {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
price?: number;
|
||||||
|
description?: string;
|
||||||
|
readonly category: string;
|
||||||
|
}
|
||||||
|
|
||||||
// 2 Schreibe eine Funktion printProductDetails, die ein Product Objekt annimmt und:
|
// 2 Schreibe eine Funktion printProductDetails, die ein Product Objekt annimmt und:
|
||||||
|
|
||||||
// Druckt die name, category und id.
|
// Druckt die name, category und id.
|
||||||
// Wenn price undefined ist, drucke "Price: Not available".
|
// Wenn price undefined ist, drucke "Price: Not available".
|
||||||
// Wenn description undefined ist, drucke "No description availabler".
|
// Wenn description undefined ist, drucke "No description availabler".
|
||||||
|
|
||||||
|
const printProductDetails = (product: Product): void => {
|
||||||
|
// let price = product.price ? product.price : 'Not avaible';
|
||||||
|
// let descr = product.description ? product.description : 'No description avaible';
|
||||||
|
let price = product.price ?? 'Not avaible';
|
||||||
|
let descr = product.description ?? 'No description avaible';
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Name: ${product.name}, Kategorie: ${product.category}, ID: ${product.id}, Preis: ${price}, Beschreibung: ${descr}`,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Aus Faulheit mit KI Objekte zum testen erstellt
|
||||||
|
|
||||||
|
// 1. Produkt mit ALLEN Eigenschaften
|
||||||
|
const fullProduct: Product = {
|
||||||
|
id: 101,
|
||||||
|
name: 'Gaming Laptop',
|
||||||
|
category: 'Elektronik',
|
||||||
|
price: 1299.99,
|
||||||
|
description: 'Leistungsstarker Laptop für Gaming und Arbeit.',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Produkt OHNE optionale Eigenschaften (weder Preis noch Beschreibung)
|
||||||
|
const minimalProduct: Product = {
|
||||||
|
id: 102,
|
||||||
|
name: 'Kaffeetasse',
|
||||||
|
category: 'Haushalt',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Produkt NUR MIT Beschreibung (ohne Preis)
|
||||||
|
const noPriceProduct: Product = {
|
||||||
|
id: 103,
|
||||||
|
name: 'Antikes Buch',
|
||||||
|
category: 'Bücher',
|
||||||
|
description: 'Ein rares Sammlerstück aus dem 19. Jahrhundert.',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tests ausführen:
|
||||||
|
printProductDetails(fullProduct); //Name: Gaming Laptop, Kategorie: Elektronik, ID: 101, Preis: 1299.99, Beschreibung: Leistungsstarker Laptop für Gaming und Arbeit.
|
||||||
|
printProductDetails(minimalProduct); //Name: Kaffeetasse, Kategorie: Haushalt, ID: 102, Preis: Not avaible, Beschreibung: No description avaible
|
||||||
|
printProductDetails(noPriceProduct); //Name: Antikes Buch, Kategorie: Bücher, ID: 103, Preis: Not avaible, Beschreibung: Ein rares Sammlerstück aus dem 19. Jahrhundert.
|
||||||
|
})();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
(() => {
|
||||||
// Übung 26: Eine verschachtelte Objekteigenschaft aktualisieren
|
// Übung 26: Eine verschachtelte Objekteigenschaft aktualisieren
|
||||||
|
|
||||||
// In dieser Übung definierst du ein Interface mit readonly und veränderbaren Eigenschaften und aktualisierst ein verschachteltes Objekt innerhalb des Interfaces. Du übst den Umgang mit verschachtelten Objekten und readonly Eigenschaften.
|
// In dieser Übung definierst du ein Interface mit readonly und veränderbaren Eigenschaften und aktualisierst ein verschachteltes Objekt innerhalb des Interfaces. Du übst den Umgang mit verschachtelten Objekten und readonly Eigenschaften.
|
||||||
@@ -5,15 +6,36 @@
|
|||||||
// Anweisungen:
|
// Anweisungen:
|
||||||
|
|
||||||
// Erstelle ein Employee Interface, die Folgendes enthält:
|
// Erstelle ein Employee Interface, die Folgendes enthält:
|
||||||
|
|
||||||
// Eine readonly employeeId (Number) Eigenschaft.
|
// Eine readonly employeeId (Number) Eigenschaft.
|
||||||
|
|
||||||
// Eine veränderbare Detail-Eigenschaft, die ein Objekt ist, das Folgendes enthält: position (String), salary (Number)
|
// Eine veränderbare Detail-Eigenschaft, die ein Objekt ist, das Folgendes enthält: position (String), salary (Number)
|
||||||
|
|
||||||
|
interface Employee {
|
||||||
|
readonly employeeId: number;
|
||||||
|
details: {
|
||||||
|
position: string;
|
||||||
|
salary: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Schreibe eine Funktion updateEmployeePosition die:
|
// Schreibe eine Funktion updateEmployeePosition die:
|
||||||
|
|
||||||
// Akzeptiert ein Employee Objekt und einen neuen position String.
|
// Akzeptiert ein Employee Objekt und einen neuen position String.
|
||||||
|
const updateEmployeePosition = (emp: Employee, pos: string) => {
|
||||||
|
emp.details.position = pos;
|
||||||
|
console.log(`ID: ${emp.employeeId}, Position: ${emp.details.position}, Salaray: ${emp.details.salary}`);
|
||||||
|
};
|
||||||
|
|
||||||
// Aktualisiert die position des Mitarbeiters, verhindert aber jede Änderung der employeeId.
|
// Aktualisiert die position des Mitarbeiters, verhindert aber jede Änderung der employeeId.
|
||||||
|
|
||||||
// Teste die Funktion, indem du ein Employee-Objekt erstellst und die Position aktualisierst.
|
// Teste die Funktion, indem du ein Employee-Objekt erstellst und die Position aktualisierst.
|
||||||
|
|
||||||
|
const employee: Employee = {
|
||||||
|
employeeId: 1,
|
||||||
|
details: {
|
||||||
|
position: 'normaler Angestellter',
|
||||||
|
salary: 1400,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateEmployeePosition(employee, 'cooler Angestellter');
|
||||||
|
})();
|
||||||
|
|||||||
@@ -5,21 +5,42 @@
|
|||||||
// Anweisungen:
|
// Anweisungen:
|
||||||
|
|
||||||
// 1 Definiere ein Car Interface, die Folgendes beinhaltet:
|
// 1 Definiere ein Car Interface, die Folgendes beinhaltet:
|
||||||
|
|
||||||
// Erforderliche Eigenschaften make (string) und model (string).
|
// Erforderliche Eigenschaften make (string) und model (string).
|
||||||
|
|
||||||
// Optionale Eigenschaften year (Number) und color (String).
|
// Optionale Eigenschaften year (Number) und color (String).
|
||||||
|
|
||||||
// Eine readonly vin (string) Eigenschaft (Fahrzeugidentifikationsnummer).
|
// Eine readonly vin (string) Eigenschaft (Fahrzeugidentifikationsnummer).
|
||||||
|
interface Car {
|
||||||
|
make: string;
|
||||||
|
model: string;
|
||||||
|
year?: number;
|
||||||
|
color?: string;
|
||||||
|
readonly vin: string;
|
||||||
|
}
|
||||||
|
|
||||||
// 2 Schreibe eine Funktion createCar die:
|
// 2 Schreibe eine Funktion createCar die:
|
||||||
|
|
||||||
// Nimmt make, model und vin auf und gibt ein Car Objekt zurück.
|
// Nimmt make, model und vin auf und gibt ein Car Objekt zurück.
|
||||||
|
|
||||||
// Optional können year und color akzeptiert werden.
|
// Optional können year und color akzeptiert werden.
|
||||||
|
|
||||||
|
type CreateCarFn = (a: string, b: string, c: string, d?: number, e?: string) => Car;
|
||||||
|
|
||||||
|
// const createCar = (make: string, model: string, vin: string, year?: number, color?: string): Car => {
|
||||||
|
// return { make, model, vin, year, color };
|
||||||
|
// };
|
||||||
|
|
||||||
|
const createCar: CreateCarFn = (make, model, vin, year, color) => {
|
||||||
|
return { make, model, vin, year, color };
|
||||||
|
};
|
||||||
|
|
||||||
// 3 Schreibe eine weitere Funktion paintCar die:
|
// 3 Schreibe eine weitere Funktion paintCar die:
|
||||||
|
|
||||||
// Akzeptiert ein Car Objekt und einen neuen color String.
|
// Akzeptiert ein Car Objekt und einen neuen color String.
|
||||||
|
|
||||||
// Aktualisiert die color des Fahrzeugs (falls vorhanden), verhindert aber jede Änderung an der vin.
|
// Aktualisiert die color des Fahrzeugs (falls vorhanden), verhindert aber jede Änderung an der vin.
|
||||||
|
const paintCar = (car: Car, color: string): Car => {
|
||||||
|
const res: Car = car;
|
||||||
|
res.color = color;
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
let car1 = createCar('VW', 'Golf', '1234', 2020, 'blau');
|
||||||
|
console.log(car1);
|
||||||
|
|
||||||
|
car1 = paintCar(car1, 'gelb');
|
||||||
|
console.log(car1);
|
||||||
|
|||||||
@@ -9,7 +9,18 @@
|
|||||||
// - make: String
|
// - make: String
|
||||||
// - model: String
|
// - model: String
|
||||||
// - year: Number
|
// - year: Number
|
||||||
|
interface Car {
|
||||||
|
make: string;
|
||||||
|
model: string;
|
||||||
|
year: number;
|
||||||
|
}
|
||||||
|
|
||||||
// 2 Schreibe eine Funktion printCarDetails, die ein Objekt vom Typ Car annimmt und die Details des Autos ausgibt (make, model und year).
|
// 2 Schreibe eine Funktion printCarDetails, die ein Objekt vom Typ Car annimmt und die Details des Autos ausgibt (make, model und year).
|
||||||
|
const printCarDetails = (car: Car) => {
|
||||||
|
console.log(`Hersteller: ${car.make}`);
|
||||||
|
console.log(`Model: ${car.model}`);
|
||||||
|
console.log(`Baujahr: ${car.year}`);
|
||||||
|
};
|
||||||
|
|
||||||
// 3 Versuche, der Funktion ein Objekt zu übergeben, das eine zusätzliche Eigenschaft hat, z. B. color. Beobachte, wie TypeScript mit der überzähligen Eigenschaft umgeht und erkläre, warum der Fehler auftritt.
|
// 3 Versuche, der Funktion ein Objekt zu übergeben, das eine zusätzliche Eigenschaft hat, z. B. color. Beobachte, wie TypeScript mit der überzähligen Eigenschaft umgeht und erkläre, warum der Fehler auftritt.
|
||||||
|
// printCarDetails({ make: 'VW', model: 'Golf', year: 2020, color: 'blue' });
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{
|
||||||
// Übung 29: Extending einer einzelnen Interface
|
// Übung 29: Extending einer einzelnen Interface
|
||||||
|
|
||||||
// In dieser Übung wirst du eine bestehende Product Interface erweitern, um einen spezifischeren Typ Electronics zu erstellen. Du wirst zusätzliche Eigenschaften für Elektronikprodukte definieren und ein Objekt des Typs Electronics erstellen.
|
// In dieser Übung wirst du eine bestehende Product Interface erweitern, um einen spezifischeren Typ Electronics zu erstellen. Du wirst zusätzliche Eigenschaften für Elektronikprodukte definieren und ein Objekt des Typs Electronics erstellen.
|
||||||
@@ -5,7 +6,32 @@
|
|||||||
// Anweisungen:
|
// Anweisungen:
|
||||||
|
|
||||||
// 1 Definiere ein Product Interface mit den Eigenschaften id, name und price.
|
// 1 Definiere ein Product Interface mit den Eigenschaften id, name und price.
|
||||||
|
interface Product {
|
||||||
|
readonly id: number;
|
||||||
|
name: string;
|
||||||
|
price: number;
|
||||||
|
}
|
||||||
|
|
||||||
// 2 Erweitere die Product Interface um eine Electronics Interface und füge die Eigenschaften brand und warranty hinzu.
|
// 2 Erweitere die Product Interface um eine Electronics Interface und füge die Eigenschaften brand und warranty hinzu.
|
||||||
|
interface Electronics extends Product {
|
||||||
|
brand: string;
|
||||||
|
warranty: number;
|
||||||
|
}
|
||||||
|
|
||||||
// 3 Erstelle ein Objekt vom Typ Electronics.
|
// 3 Erstelle ein Objekt vom Typ Electronics.
|
||||||
|
const comp: Electronics = {
|
||||||
|
id: 1,
|
||||||
|
name: 'Computer',
|
||||||
|
price: 1499.99,
|
||||||
|
brand: 'Asus',
|
||||||
|
warranty: 24,
|
||||||
|
};
|
||||||
|
|
||||||
|
const konsole: Electronics = {
|
||||||
|
id: 2,
|
||||||
|
name: 'Steam',
|
||||||
|
price: 1039.99,
|
||||||
|
brand: 'Valve',
|
||||||
|
warranty: 24,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
// Übung 30: Einzigartige Eigenschaften zu einem Subtyp hinzufügen
|
{
|
||||||
|
|
||||||
// In dieser Übung wirst du die Product Interface erweitern, um einen Typ Furniture zu erstellen, der spezielle Eigenschaften für Möbel hinzufügt, wie material und dimensions.
|
|
||||||
|
|
||||||
// Anweisungen:
|
|
||||||
|
|
||||||
// 1 Erweitere die Product Interface, um eine Furniture Interface zu erstellen.
|
// 1 Erweitere die Product Interface, um eine Furniture Interface zu erstellen.
|
||||||
|
interface Product {
|
||||||
|
readonly id: number;
|
||||||
|
name: string;
|
||||||
|
price: number;
|
||||||
|
}
|
||||||
|
|
||||||
// 2 Füge die Eigenschaften material und dimensions zu den Möbeln hinzu.
|
// 2 Füge die Eigenschaften material und dimensions zu den Möbeln hinzu.
|
||||||
|
interface Furniture extends Product {
|
||||||
|
material: string;
|
||||||
|
dimensions: { length: number; width: number; height: number };
|
||||||
|
}
|
||||||
|
|
||||||
// 3 Erstelle ein Objekt vom Typ Furniture, das ein Möbelstück darstellt.
|
// 3 Erstelle ein Objekt vom Typ Furniture, das ein Möbelstück darstellt.
|
||||||
|
const schrank: Furniture = {
|
||||||
|
id: 2,
|
||||||
|
name: 'Schrank',
|
||||||
|
price: 199.99,
|
||||||
|
material: 'Holz',
|
||||||
|
dimensions: { length: 0.7, width: 1.5, height: 1.7 },
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(JSON.stringify(schrank, null, 2));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{
|
||||||
// Übung 31: Generic Functions für Arrays
|
// Übung 31: Generic Functions für Arrays
|
||||||
|
|
||||||
// In dieser Übung erstellst du eine generische Funktion, die mit Arrays beliebigen Typs funktioniert. Du definierst eine Funktion, die das erste Element des Arrays zurückgibt und es TypeScript ermöglicht, den Elementtyp aus dem übergebenen Array abzuleiten.
|
// In dieser Übung erstellst du eine generische Funktion, die mit Arrays beliebigen Typs funktioniert. Du definierst eine Funktion, die das erste Element des Arrays zurückgibt und es TypeScript ermöglicht, den Elementtyp aus dem übergebenen Array abzuleiten.
|
||||||
@@ -5,5 +6,18 @@
|
|||||||
// Anweisungen:
|
// Anweisungen:
|
||||||
|
|
||||||
// 1 Definiere eine generische Funktion getFirstElement, die ein Array beliebigen Typs annimmt und das erste Element oder undefined zurückgibt, wenn das Array leer ist.
|
// 1 Definiere eine generische Funktion getFirstElement, die ein Array beliebigen Typs annimmt und das erste Element oder undefined zurückgibt, wenn das Array leer ist.
|
||||||
|
const getFirstElement = <T>(arg: T[]): T => {
|
||||||
|
return arg[0];
|
||||||
|
};
|
||||||
|
|
||||||
// 2 Rufe die Funktion mit Arrays verschiedener Typen auf (number[], string[], etc.) und protokolliere das Ergebnis.
|
// 2 Rufe die Funktion mit Arrays verschiedener Typen auf (number[], string[], etc.) und protokolliere das Ergebnis.
|
||||||
|
console.log(getFirstElement<number>([1, 2, 3]));
|
||||||
|
console.log(getFirstElement<string>(['a', 'b', 'c']));
|
||||||
|
console.log(
|
||||||
|
getFirstElement<object>([
|
||||||
|
{ name: 'a', id: 1 },
|
||||||
|
{ name: 'b', id: 2 },
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
console.log(getFirstElement([]));
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Dabei werden die Typen ihrer Eingänge (Parameter) und ihres Ausgangs (Rückgabe
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
{
|
{
|
||||||
type MathFn = (x: number, y: number) => number; // Funktionstype-deklaration
|
type MathFn = (x: number, y: number) => number; // Funktionstyp-deklaration
|
||||||
|
|
||||||
const mathOperationFn = (fn: MathFn): number => {
|
const mathOperationFn = (fn: MathFn): number => {
|
||||||
return fn(4, 5);
|
return fn(4, 5);
|
||||||
@@ -15,6 +15,10 @@ Dabei werden die Typen ihrer Eingänge (Parameter) und ihres Ausgangs (Rückgabe
|
|||||||
return a * b;
|
return a * b;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function multiplyFn(a: number, b: number): number {
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
const multiply2: MathFn = (a, b) => {
|
const multiply2: MathFn = (a, b) => {
|
||||||
return a * b;
|
return a * b;
|
||||||
};
|
};
|
||||||
@@ -52,9 +56,9 @@ Dabei werden die Typen ihrer Eingänge (Parameter) und ihres Ausgangs (Rückgabe
|
|||||||
console.log(message);
|
console.log(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// RECOMMENDED
|
||||||
type LogFn = (param: string) => void;
|
type LogFn = (param: string) => void;
|
||||||
|
|
||||||
// RECOMMENDED
|
|
||||||
const logMessage3: LogFn = (message) => {
|
const logMessage3: LogFn = (message) => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,23 @@ Eine Call Signature ermöglicht es dir, die Art und Weise zu beschreiben, wie ei
|
|||||||
console.log(multiply(25, 25)); //=> 625
|
console.log(multiply(25, 25)); //=> 625
|
||||||
|
|
||||||
//--------
|
//--------
|
||||||
|
|
||||||
|
type Person = {
|
||||||
|
name: string;
|
||||||
|
age: number;
|
||||||
|
walk: (speed?: number) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const person: Person = {
|
||||||
|
name: 'John',
|
||||||
|
age: 23,
|
||||||
|
walk() {
|
||||||
|
return "i'm walking.";
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(person.walk());
|
||||||
|
|
||||||
// VORSICHT SONDERFALL in der Signatur bei describable Function
|
// VORSICHT SONDERFALL in der Signatur bei describable Function
|
||||||
|
|
||||||
// Bei Describable Function : anstatt => für Rückgabe
|
// Bei Describable Function : anstatt => für Rückgabe
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ Destrukturierung vereinfacht deinen Code, indem du Werte direkt aus Objekten ode
|
|||||||
console.log(`Hello, ${name}! You are ${age} years old.`);
|
console.log(`Hello, ${name}! You are ${age} years old.`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// NEGATIV BEISPIEL - NOT RECOMMENDED
|
||||||
const greetUser3: (o: { name: string; age: number }) => void = (
|
const greetUser3: (o: { name: string; age: number }) => void = (
|
||||||
{
|
{
|
||||||
name,
|
name,
|
||||||
@@ -37,6 +38,17 @@ Destrukturierung vereinfacht deinen Code, indem du Werte direkt aus Objekten ode
|
|||||||
|
|
||||||
greetUser4({ name: 'Alice', age: 25 });
|
greetUser4({ name: 'Alice', age: 25 });
|
||||||
|
|
||||||
|
// RECOMMENDED ALTERNATIVE
|
||||||
|
type User = { name: string; age: number };
|
||||||
|
|
||||||
|
const greetUser5 = (user: User) => {
|
||||||
|
const { name, age } = user;
|
||||||
|
|
||||||
|
console.log(`Hello, ${name}! You are ${age} years old.`);
|
||||||
|
};
|
||||||
|
|
||||||
|
greetUser5({ name: 'Alice', age: 25 });
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
const sum = ({ a, b, c }: { a: number; b: number; c: number }): void => {
|
const sum = ({ a, b, c }: { a: number; b: number; c: number }): void => {
|
||||||
console.log(a + b + c);
|
console.log(a + b + c);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
In TypeScript gibt es drei spezielle Typen: never, unknown und void. Diese Typen werden in bestimmten Situationen verwendet, um bestimmte Verhaltensweisen zu modellieren.
|
In TypeScript gibt es drei spezielle Typen: never, unknown und void. Diese Typen werden in bestimmten Situationen verwendet, um bestimmte Verhaltensweisen zu modellieren.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// - void wird für Funktionen verwendet, die keinen Wert zurückgeben, was bedeutet, dass die Funktion eine Aktion ausführt, ohne ein Ergebnis zu liefern. Es wird kein Ergebnis erwartet, dennoch können Ergebnisse zurückgegeben werden ohne Fehlermeldung
|
// - void wird für Funktionen verwendet, die keinen Wert zurückgeben, was bedeutet, dass die Funktion eine Aktion ausführt, ohne ein Ergebnis zu liefern. Es wird kein Ergebnis erwartet, dennoch können Ergebnisse zurückgegeben werden ohne Fehlermeldung, wenn void in einer "call Signature" (Funktionstyp-Deklaration verwendet wird)
|
||||||
|
|
||||||
// - unknown ist eine sicherere Alternative zu any und erfordert Type-Checking, bevor Operationen mit Werten unbekannten Typs durchgeführt werden, wodurch Laufzeitfehler vermieden werden.
|
// - unknown ist eine sicherere Alternative zu any und erfordert Type-Checking, bevor Operationen mit Werten unbekannten Typs durchgeführt werden, wodurch Laufzeitfehler vermieden werden.
|
||||||
|
|
||||||
@@ -16,9 +16,9 @@ In TypeScript gibt es drei spezielle Typen: never, unknown und void. Diese Typen
|
|||||||
const logMessage = (message: string): void => {
|
const logMessage = (message: string): void => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
const logMessage2: LogFn = (message) => {
|
const logMessage2: LogFn = (message) => {
|
||||||
console.log(message);
|
console.log(message);
|
||||||
return 'message';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
logMessage('Hello, TypeScript!');
|
logMessage('Hello, TypeScript!');
|
||||||
@@ -114,6 +114,7 @@ In TypeScript gibt es drei spezielle Typen: never, unknown und void. Diese Typen
|
|||||||
return 'Handling a circle';
|
return 'Handling a circle';
|
||||||
case 'square':
|
case 'square':
|
||||||
return 'Handling a square';
|
return 'Handling a square';
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// throw new Error("shape doesn't exist");
|
// throw new Error("shape doesn't exist");
|
||||||
const _exhaustiveCheck: never = shape; // TypeScript error if a new shape is added
|
const _exhaustiveCheck: never = shape; // TypeScript error if a new shape is added
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ In TypeScript kannst du die Modifizierer für Eigenschaften in Objekten verwende
|
|||||||
// ?? operator => nullish coalescing
|
// ?? operator => nullish coalescing
|
||||||
const printBookInfo = (book: Book) => {
|
const printBookInfo = (book: Book) => {
|
||||||
const author = book.author ?? 'Unknown'; // Use "Unknown" if author is undefined
|
const author = book.author ?? 'Unknown'; // Use "Unknown" if author is undefined
|
||||||
|
const author2 = book.author ? book.author : 'Unknown'; // Use "Unknown" if author is undefined
|
||||||
const pages = book.pages ?? 'Page count not available';
|
const pages = book.pages ?? 'Page count not available';
|
||||||
console.log(`Title: ${book.title}, Author: ${author}, Pages: ${pages}`);
|
console.log(`Title: ${book.title}, Author: ${author}, Pages: ${pages}`);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
type StringNumberMap = Record<string, number>;
|
type StringNumberMap = Record<string, number>;
|
||||||
|
|
||||||
const numbers: StringNumberMap = {
|
const numbers: StringNumberMap = {
|
||||||
one: 1,
|
true: 1,
|
||||||
two: 2,
|
two: 2,
|
||||||
three: 3,
|
three: 3,
|
||||||
};
|
};
|
||||||
|
|||||||
5
04_typescript/unterricht/tag35/README.md
Normal file
5
04_typescript/unterricht/tag35/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Projektarbeit & Exkurs
|
||||||
|
|
||||||
|
- Abschlussquiz
|
||||||
|
- **Exkurs Webprojekt in TS mit KI Part I**
|
||||||
|
- 1:1 Meeting
|
||||||
6
04_typescript/unterricht/tag35/meeting.md
Normal file
6
04_typescript/unterricht/tag35/meeting.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Meeting Fr. 31.07.2026
|
||||||
|
|
||||||
|
- [ ] 14:20 - 14:30 Uhr Adel
|
||||||
|
- [ ] 14:30 - 14:40 Uhr Kahleel
|
||||||
|
- [ ] 14:40 - 14:50 Uhr Ersin
|
||||||
|
- [ ] 14:50 - 15:00 Uhr Andreas
|
||||||
8
webseite-ts-claude/backend/.vscode/settings.json
vendored
Normal file
8
webseite-ts-claude/backend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"workbench.colorCustomizations": {
|
||||||
|
"titleBar.activeForeground": "#333",
|
||||||
|
"titleBar.activeBackground": "#d0d06b",
|
||||||
|
"titleBar.inactiveForeground": "#ddd",
|
||||||
|
"titleBar.inactiveBackground": "#a4a464"
|
||||||
|
}
|
||||||
|
}
|
||||||
68
webseite-ts-claude/backend/api.rest
Normal file
68
webseite-ts-claude/backend/api.rest
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# BROWSE Products
|
||||||
|
|
||||||
|
GET http://127.0.0.1:8000/api/products
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# READ Product
|
||||||
|
|
||||||
|
GET http://127.0.0.1:8000/api/products/c38913d4-9524-461f-85d2-525241166e8e
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# ADD Product
|
||||||
|
|
||||||
|
POST http://127.0.0.1:8000/api/products
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Testproduct",
|
||||||
|
"price": 9.99
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# UPDATE Product
|
||||||
|
|
||||||
|
PUT http://127.0.0.1:8000/api/products
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"_id": "940c316b-518c-4a18-b1f6-328e010eef4d",
|
||||||
|
"name": "Testproduct Updated",
|
||||||
|
"price": 123.22
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# DELETE Product
|
||||||
|
|
||||||
|
DELETE http://127.0.0.1:8000/api/products/6cbe4783-cfb5-4ed7-8196-9d6b55217de0
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# RESET Products
|
||||||
|
PATCH http://127.0.0.1:8000/api/products/reset
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"reset": true
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
# SAVE Products
|
||||||
|
PATCH http://127.0.0.1:8000/api/products/save
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"save": true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
38
webseite-ts-claude/backend/data/products.bakup.json
Normal file
38
webseite-ts-claude/backend/data/products.bakup.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"_id": "6cbe4783-cfb5-4ed7-8196-9d6b55217de0",
|
||||||
|
"position": 1,
|
||||||
|
"name": "3Doodler 3D Printing Pen",
|
||||||
|
"price": 29.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "c38913d4-9524-461f-85d2-525241166e8e",
|
||||||
|
"position": 2,
|
||||||
|
"name": "Powerstation 5- E. Maximus Chargus",
|
||||||
|
"price": 44.95
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "cdc887bd-fb0e-4485-8516-0a3720c3bb72",
|
||||||
|
"position": 3,
|
||||||
|
"name": "8-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 6.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "bded2710-8db8-41a5-87af-269195eab449",
|
||||||
|
"position": 4,
|
||||||
|
"name": "16-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 12.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "3b3352c1-789d-4b4f-8a93-f05f44ece5a5",
|
||||||
|
"position": 5,
|
||||||
|
"name": "32-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 18.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "5af39d33-cbcd-42c7-a160-50084faa91ba",
|
||||||
|
"position": 6,
|
||||||
|
"name": "64-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 21.99
|
||||||
|
}
|
||||||
|
]
|
||||||
38
webseite-ts-claude/backend/data/products.json
Normal file
38
webseite-ts-claude/backend/data/products.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"_id": "6cbe4783-cfb5-4ed7-8196-9d6b55217de0",
|
||||||
|
"position": 1,
|
||||||
|
"name": "3Doodler 3D Printing Pen",
|
||||||
|
"price": 29.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "c38913d4-9524-461f-85d2-525241166e8e",
|
||||||
|
"position": 2,
|
||||||
|
"name": "Powerstation 5- E. Maximus Chargus",
|
||||||
|
"price": 44.95
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "cdc887bd-fb0e-4485-8516-0a3720c3bb72",
|
||||||
|
"position": 3,
|
||||||
|
"name": "8-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 6.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "bded2710-8db8-41a5-87af-269195eab449",
|
||||||
|
"position": 4,
|
||||||
|
"name": "16-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 12.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "3b3352c1-789d-4b4f-8a93-f05f44ece5a5",
|
||||||
|
"position": 5,
|
||||||
|
"name": "32-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 18.99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "5af39d33-cbcd-42c7-a160-50084faa91ba",
|
||||||
|
"position": 6,
|
||||||
|
"name": "64-Bit Legendary Hero Heat-Change Mug",
|
||||||
|
"price": 21.99
|
||||||
|
}
|
||||||
|
]
|
||||||
113
webseite-ts-claude/backend/model/ProductModel.js
Normal file
113
webseite-ts-claude/backend/model/ProductModel.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import { v4 as id } from 'uuid';
|
||||||
|
|
||||||
|
// Factoryfunction
|
||||||
|
const ProductModel = () => {
|
||||||
|
let products = [];
|
||||||
|
|
||||||
|
const load = () => {
|
||||||
|
try {
|
||||||
|
products = JSON.parse(fs.readFileSync('./data/products.json', 'utf-8'));
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Something went wrong: ', error);
|
||||||
|
products = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
try {
|
||||||
|
products = JSON.parse(fs.readFileSync('./data/products.bakup.json', 'utf-8'));
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Something went wrong: ', error);
|
||||||
|
products = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
try {
|
||||||
|
fs.writeFileSync('./data/products.json', JSON.stringify(products, null, 2), 'utf-8');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Something went wrong:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAll = () => {
|
||||||
|
return products;
|
||||||
|
};
|
||||||
|
|
||||||
|
const get = (id) => {
|
||||||
|
const product = products.find((product) => {
|
||||||
|
return product._id === id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Guard
|
||||||
|
if (!product) return null;
|
||||||
|
|
||||||
|
return product;
|
||||||
|
};
|
||||||
|
|
||||||
|
const add = (product) => {
|
||||||
|
const { name, price } = product;
|
||||||
|
|
||||||
|
if (!hasAllProps(product)) return false;
|
||||||
|
|
||||||
|
products.push({
|
||||||
|
_id: id(),
|
||||||
|
name,
|
||||||
|
price: Number(price.toFixed(2)),
|
||||||
|
position: products.length + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const update = (product) => {
|
||||||
|
const foundIdx = products.findIndex((obj) => {
|
||||||
|
return obj._id === product._id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Guard
|
||||||
|
if (foundIdx === -1 || !hasAllProps(product)) return false;
|
||||||
|
|
||||||
|
products[foundIdx] = product;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const remove = (id) => {
|
||||||
|
const foundIdx = products.findIndex((obj) => {
|
||||||
|
return obj._id === id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Guard
|
||||||
|
if (foundIdx === -1) return false;
|
||||||
|
|
||||||
|
products.splice(foundIdx, 1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hasAllProps - Überprüfung ob Product "name" und "price"
|
||||||
|
* @return boolean
|
||||||
|
**/
|
||||||
|
const hasAllProps = (product) => {
|
||||||
|
// return Object.hasOwn(product, 'name') && Object.hasOwn(product, 'price');
|
||||||
|
|
||||||
|
const keys = Object.keys(product); // => ['name', 'price']
|
||||||
|
return keys.includes('name') && keys.includes('price');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
// load: load
|
||||||
|
load,
|
||||||
|
save,
|
||||||
|
add,
|
||||||
|
getAll,
|
||||||
|
get,
|
||||||
|
update,
|
||||||
|
delete: remove,
|
||||||
|
reset,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProductModel;
|
||||||
20
webseite-ts-claude/backend/package.json
Normal file
20
webseite-ts-claude/backend/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "website-backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "npx nodemon server.js",
|
||||||
|
"server": "npx nodemon server.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"type": "module",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^5.6.2",
|
||||||
|
"cors": "^2.8.6",
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"uuid": "^14.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
158
webseite-ts-claude/backend/server.js
Normal file
158
webseite-ts-claude/backend/server.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import color from 'chalk';
|
||||||
|
import cors from 'cors';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
|
||||||
|
dotenv.config(); // Sensible Konfigurationsvariablen in .env Datei auslagern
|
||||||
|
|
||||||
|
// Model
|
||||||
|
import ProductModel from './model/ProductModel.js'; // Dateiendung nicht vergessen
|
||||||
|
|
||||||
|
const PORT = process.env.SERVER_PORT; // || 8000;
|
||||||
|
const HOST = process.env.SERVER_HOST; // || '127.0.0.1'; // 'localhost'
|
||||||
|
const BASE_URL = `http://${HOST}:${PORT}`;
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const products = ProductModel();
|
||||||
|
|
||||||
|
const corsOptions = {
|
||||||
|
origin: ['http://localhost:3000', 'http://127.0.0.1:3000'],
|
||||||
|
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
|
||||||
|
};
|
||||||
|
|
||||||
|
// lade Produkte
|
||||||
|
products.load();
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors(corsOptions)); // Fängt alle HTTP-Requests vor den Routenabfragen ab.
|
||||||
|
app.use(express.json()); // HTTP-Request Body wird als JSON Objekt erwartet und geparsed wird. // app.use(bodyParser.json()) - Modul body-parser war in express 4 notwendig
|
||||||
|
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
console.log(color.yellow('HTTP-Method: '), color.magenta(req.method));
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
// console.log(req.headers);
|
||||||
|
|
||||||
|
res.send('SERVER WORKS!'); // HTTP-Response wird gesendet.
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/persons', (req, res) => {
|
||||||
|
// res.setHeader('Content-Type', 'application/json'); // nicht notwendig den Content-Type selber zu definieren, da das express automatisch zuweist.
|
||||||
|
|
||||||
|
const persons = [
|
||||||
|
{ firstName: 'John', lastName: 'Wick' },
|
||||||
|
{ firstName: 'Jane', lastName: 'Doe' },
|
||||||
|
{ firstName: 'Max', lastName: 'Mustermann' },
|
||||||
|
];
|
||||||
|
// res.send(JSON.stringify(persons)); // nicht notwendig JS Objekte in JSON Strings umzuwandeln, das das express automatisch umwandelt
|
||||||
|
|
||||||
|
res.send(persons);
|
||||||
|
});
|
||||||
|
|
||||||
|
// REST API ======
|
||||||
|
// Representational State Transfer (abgekürzt REST)
|
||||||
|
// GET - Daten Holen
|
||||||
|
// POST - Daten hinzufügen
|
||||||
|
// PUT/PATCH - Daten aktualisieren / Einen Datensatz aktualisieren
|
||||||
|
// DELETE - Daten löschen
|
||||||
|
|
||||||
|
// HTTP - Methode - POST
|
||||||
|
// (C)RUD - create - neues Produkt erstellen
|
||||||
|
// BRE(A)D - add
|
||||||
|
app.post('/api/products', (req, res) => {
|
||||||
|
const product = req.body;
|
||||||
|
|
||||||
|
if (!products.add(product)) {
|
||||||
|
return res.status(400).send({ msg: 'Could not add product', status: 400, success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send({ msg: 'Product added', status: 200, success: true, data: product });
|
||||||
|
});
|
||||||
|
|
||||||
|
// HTTP - Methode - GET
|
||||||
|
// C(R)UD - read
|
||||||
|
// (B)READ - browse
|
||||||
|
app.get('/api/products', (req, res) => {
|
||||||
|
const data = products.getAll();
|
||||||
|
return res.send(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// HTTP - Methode - GET
|
||||||
|
// C(R)UD - read
|
||||||
|
// B(R)EAD - read
|
||||||
|
app.get('/api/products/:id', (req, res) => {
|
||||||
|
const id = req.params.id; // routenparameter :id wird als Eigenschaft in params-Objekt abgelegt
|
||||||
|
const product = products.get(id);
|
||||||
|
|
||||||
|
if (!product) {
|
||||||
|
return res.status(400).send({ msg: 'Could not get product', status: 400, success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send(product);
|
||||||
|
});
|
||||||
|
|
||||||
|
// HTTP - Methode - PUT/PATCH
|
||||||
|
// CR(U)D - update
|
||||||
|
// BR(E)AD - edit
|
||||||
|
app.put('/api/products', (req, res) => {
|
||||||
|
const product = req.body;
|
||||||
|
|
||||||
|
const updated = products.update(product);
|
||||||
|
|
||||||
|
if (!updated) {
|
||||||
|
return res.status(400).send({ msg: 'Could not update product', status: 400, success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send({ msg: 'Product updated', status: 200, success: true, data: product });
|
||||||
|
});
|
||||||
|
|
||||||
|
// HTTP - Methode - PATCH
|
||||||
|
// reseten von Produkten
|
||||||
|
app.patch('/api/products/reset', (req, res) => {
|
||||||
|
const reset = req.body.reset;
|
||||||
|
|
||||||
|
if (!reset) {
|
||||||
|
return res.status(400).send({ msg: 'Could not reset products', status: 400, success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
products.reset();
|
||||||
|
|
||||||
|
return res.send({ msg: 'Products reseted', status: 200, success: true, data: reset });
|
||||||
|
});
|
||||||
|
|
||||||
|
// HTTP - Methode - PATCH
|
||||||
|
// speichern von Produkten
|
||||||
|
app.patch('/api/products/save', (req, res) => {
|
||||||
|
const save = req.body.save;
|
||||||
|
|
||||||
|
if (!save) {
|
||||||
|
return res.status(400).send({ msg: 'Could not save products', status: 400, success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
products.save();
|
||||||
|
|
||||||
|
return res.send({ msg: 'Products saved', status: 200, success: true, data: save });
|
||||||
|
});
|
||||||
|
|
||||||
|
// HTTP - Methode - DELETE
|
||||||
|
// CRU(D) - delete
|
||||||
|
// BREA(D) - delete
|
||||||
|
app.delete('/api/products/:id', (req, res) => {
|
||||||
|
const id = req.params.id;
|
||||||
|
|
||||||
|
if (!products.delete(id)) {
|
||||||
|
return res.status(400).send({ msg: 'Could not delete product', status: 400, success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.send({ msg: 'Product deleted', status: 200, success: true, data: id });
|
||||||
|
});
|
||||||
|
|
||||||
|
// =========
|
||||||
|
|
||||||
|
app.listen(PORT, HOST, () => {
|
||||||
|
console.log(color.magenta(`🚀 Server is running at: ${BASE_URL}`));
|
||||||
|
console.log(color.yellow('CTRL + C to close.'));
|
||||||
|
});
|
||||||
19
webseite-ts-claude/backend/utils/json-modifier.js
Normal file
19
webseite-ts-claude/backend/utils/json-modifier.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import { v4 as id } from 'uuid';
|
||||||
|
|
||||||
|
console.log(id()); // 97412ca6-c7a8-4868-a49b-3cf4c2258dd8
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(fs.readFileSync('../data/products.json', 'utf-8'));
|
||||||
|
|
||||||
|
const products = data.map((product, index) => {
|
||||||
|
return { _id: product._id || id(), position: index + 1, ...product };
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(products);
|
||||||
|
|
||||||
|
fs.writeFileSync('../data/products.json', JSON.stringify(products, null, 2), 'utf-8');
|
||||||
|
console.log('File modified');
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
28
webseite-ts-claude/backend/utils/package-lock.json
generated
Normal file
28
webseite-ts-claude/backend/utils/package-lock.json
generated
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "utils",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "utils",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": "^14.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "14.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz",
|
||||||
|
"integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist-node/bin/uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
webseite-ts-claude/backend/utils/package.json
Normal file
14
webseite-ts-claude/backend/utils/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "utils",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "json-modifier.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": "^14.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
webseite-ts-claude/frontend/.vscode/settings.json
vendored
Normal file
8
webseite-ts-claude/frontend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"workbench.colorCustomizations": {
|
||||||
|
"titleBar.activeForeground": "#333",
|
||||||
|
"titleBar.activeBackground": "#86cd8b",
|
||||||
|
"titleBar.inactiveForeground": "#ddd",
|
||||||
|
"titleBar.inactiveBackground": "#6fa973"
|
||||||
|
}
|
||||||
|
}
|
||||||
33
webseite-ts-claude/frontend/README.md
Normal file
33
webseite-ts-claude/frontend/README.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Aufgabe
|
||||||
|
|
||||||
|
- Nach Positionsveränderung (drop, move-up,move-down) sollen alle Positionen der Zeilen als Array festgehalten werden.
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
const positions = [
|
||||||
|
{
|
||||||
|
_id: '6cbe4783-cfb5-4ed7-8196-9d6b55217de0',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: '6cbe4783-cfb5-4ed7-8196-9d6b55217de0',
|
||||||
|
position: 2,
|
||||||
|
},
|
||||||
|
{...}
|
||||||
|
]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- Positionsarray an Server zur Aktualisierung senden (PUT)
|
||||||
|
|
||||||
|
```js
|
||||||
|
'http://127.0.0.1:8000/api/products/positions';
|
||||||
|
```
|
||||||
|
|
||||||
|
- Server: Positionen abfangen und products Array aktualisieren
|
||||||
|
|
||||||
|
```js
|
||||||
|
//ProductModel
|
||||||
|
updatePosition() {...}
|
||||||
|
|
||||||
|
```
|
||||||
12870
webseite-ts-claude/frontend/assets/css/main.css
Normal file
12870
webseite-ts-claude/frontend/assets/css/main.css
Normal file
File diff suppressed because it is too large
Load Diff
1
webseite-ts-claude/frontend/assets/css/main.css.map
Normal file
1
webseite-ts-claude/frontend/assets/css/main.css.map
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.1 KiB |
64
webseite-ts-claude/frontend/assets/img/donkey-logo.svg
Normal file
64
webseite-ts-claude/frontend/assets/img/donkey-logo.svg
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 1613 1368" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1491.62,327.703C1541.78,287.116 1594.34,248.113 1644.54,207.788L1618.71,488.181L1461.89,715C1476.02,740.352 1490.29,765.63 1504.68,790.833C1510.26,800.644 1520.89,820.767 1526.87,829.305C1522.43,842.281 1517.85,861.574 1514.38,875.237L1481.26,875.044C1484.81,860.981 1488.21,846.879 1491.44,832.74C1482.68,815.587 1470.33,796.564 1460.45,779.711C1455.18,770.719 1443.35,749.908 1437.18,743.219C1431.97,737.568 1413.75,723.823 1406.86,718.38L1349.17,672.785C1340.15,665.605 1325.81,654.677 1317.65,646.931C1344.15,658.866 1370.79,670.495 1397.55,681.814C1408.37,686.436 1424.93,694.156 1435.63,697.917C1446.75,678.509 1459.42,658.824 1471.18,639.729L1535.44,535.72C1545.43,519.643 1561.06,495.702 1569.9,479.652C1573.42,473.259 1576.75,431.467 1577.96,421.486C1582.65,383.079 1587.83,344.732 1593.48,306.455C1572.91,321.935 1550.73,340.775 1530.53,357.115C1511.75,410.62 1494.1,470.148 1476.2,524.616L1447.71,611.181C1443.15,625.019 1436.57,643.125 1432.83,656.866L1431.54,651.473C1433.84,641.445 1435.28,631.347 1437.1,621.239C1453.43,530.598 1468.66,439.697 1486.96,349.43C1488.43,342.142 1489.83,334.923 1491.62,327.703Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M916.43,207.704C961.199,241.308 1004.53,277.533 1048.89,311.731C1055.75,317.026 1063.3,322.84 1069.76,328.558C1084.91,402.681 1098.82,477.052 1111.5,551.638L1123.71,623.629C1124.84,630.024 1127.8,649.079 1129.5,654.246L1127.21,654.902L1064.01,461.07L1043.22,395.892C1039.36,383.693 1034.98,368.156 1030.22,356.723L966.963,305.869C973.851,345.434 977.161,389.979 983.271,429.912C984.436,437.525 987.443,470.367 989.214,475.458C993.012,486.381 1011.45,512.702 1018.59,524.428L1125.74,698.168C1162.02,682.956 1198.1,667.285 1233.99,651.16L1236.23,653.085L1159.8,713.519C1149.67,721.504 1131.52,734.758 1123.15,743.745C1119.95,747.171 1107.65,768.564 1104.75,773.47C1092.95,793.324 1081.27,813.256 1069.73,833.265C1072.83,845.948 1077.43,862.679 1079.73,875.203L1045.92,874.941C1042.48,860.188 1038.86,843.686 1034.67,829.252C1055.31,792.12 1079.95,751.725 1099.25,714.842L1000.82,573.175L965.228,521.882C957.797,511.147 949.566,500.6 943.536,489.005C939.585,481.409 918.918,241.006 916.43,207.704Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1219.69,1043.31C1216.9,1055.11 1215.25,1071.34 1213.71,1083.51C1211.47,1101.52 1209.04,1119.52 1206.41,1137.48C1213.84,1129.75 1221.59,1120.73 1228.78,1112.67C1231.48,1113.88 1231.74,1114.1 1234.2,1115.86C1224.59,1135.46 1214.83,1154.98 1204.91,1174.42C1201.52,1181.1 1198.22,1187.99 1194.72,1194.6C1185.82,1211.39 1181.67,1229.44 1176.43,1247.66C1169.12,1273.61 1161.65,1299.52 1154.02,1325.38C1183.18,1357.1 1211.08,1390.67 1240.43,1422.03L1320.27,1422.15C1349.41,1390.02 1378.35,1357.69 1407.07,1325.19C1399.71,1300.58 1392.6,1275.89 1385.75,1251.13C1382.8,1240.65 1376.83,1217.65 1372.78,1208.49C1356.12,1170.8 1333.4,1132.86 1316.7,1095.31C1329.42,1109.42 1341.94,1123.71 1354.25,1138.19C1352.41,1113.22 1345.93,1068.33 1341.47,1043.62C1364.43,1071.12 1363.55,1075.68 1374.28,1109.56L1400.59,1194.79C1402.94,1182.51 1407.12,1168.91 1410.29,1156.72C1417.49,1129.01 1425.33,1101.5 1432.42,1073.75C1444.98,1069.98 1457.6,1066.39 1470.27,1062.97C1461.28,1104.6 1445.83,1150.13 1435.34,1191.93C1431.14,1208.63 1421.72,1235.52 1420.04,1252.56C1419.68,1256.27 1426.98,1280.67 1428.52,1286.09L1441.6,1333.45C1413.47,1364.5 1386.31,1396.69 1358.3,1427.87C1350.61,1436.42 1342.63,1445.37 1335.54,1454.4L1225.48,1454.07L1160.83,1380.6C1149.19,1367.45 1129.53,1346.5 1119.24,1332.85L1142.09,1251.77C1130.78,1209.15 1118.57,1167.25 1106.94,1124.76C1101.69,1105.59 1094.99,1082.4 1091.02,1063.09C1103.38,1067.19 1115.9,1070.76 1128.57,1073.78C1138.21,1113.81 1150.3,1154.52 1160.43,1194.92L1185.93,1112.1C1190.27,1098.12 1195.43,1080 1201.13,1066.93C1202.5,1063.8 1216.75,1046.89 1219.69,1043.31Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M964.12,882.509L1203.79,882.435C1217.38,902.737 1235.33,933.285 1249.05,951.826C1240.58,972.332 1233.36,996.483 1222.71,1015.55C1221.53,1017.65 1214.19,1024.7 1212.12,1026.69C1201.69,1037.2 1191.13,1047.59 1180.46,1057.85C1171.71,1059.38 1138.99,1066.24 1131.78,1064.48C1105.56,1058.1 1076.73,1049.63 1050.67,1042.53C1031.66,1005.96 1009.14,961.987 989.029,926.505C980.644,924.78 972.242,923.139 963.824,921.582C963.805,908.9 963.526,895.083 964.12,882.509Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1363.24,882.527C1413.82,881.563 1464.41,883.283 1514.99,882.818C1542.28,882.568 1569.55,882.616 1596.84,883.007C1596.99,895.794 1597.02,908.583 1596.92,921.372C1588.42,923.144 1579.8,925.134 1571.31,927.006C1555.33,960.56 1531.67,1001.9 1513.92,1035.13L1510.06,1042.61C1484.37,1050.37 1455.74,1058.27 1429.75,1064.06C1423.09,1065.55 1387.96,1060.57 1382.29,1057.05C1369.21,1048.92 1351.36,1027.59 1338.82,1017C1328.5,996.763 1320.31,972.735 1312.25,951.658C1325.56,930.572 1341.46,904.817 1355.99,884.15C1357.42,882.113 1360.47,882.626 1363.24,882.527Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1105.1,451.694C1123.75,471.025 1143.51,493.81 1161.62,513.944L1244.79,605.476C1268.06,605.928 1292.65,605.542 1316,605.557C1329.24,590.043 1345.43,573.176 1359.32,557.815L1454.77,452.524L1426.18,548.115C1423.29,557.767 1414.33,590.084 1410.7,596.764C1401.5,613.659 1385.71,636.678 1374.75,653.088C1382.9,622.923 1391.82,593.845 1400.53,563.928C1377.46,588.467 1353.94,614.808 1330.97,639.762C1318.71,637.771 1286.42,630.483 1276.22,631.425C1266.71,632.305 1240.73,637.673 1230.02,639.563C1208.22,616.643 1187.58,592.215 1165.29,569.201L1163.39,573.58C1170.4,593.856 1181.29,631.819 1186.17,652.325C1179.34,642.071 1153.56,606.025 1150.41,596.668C1135.2,551.472 1118.33,497.113 1105.1,451.694Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1163.39,573.58C1162.86,571.669 1161.12,567.514 1161.62,565.744C1162.01,564.355 1165.14,568.964 1165.29,569.201L1163.39,573.58Z" style="fill:rgb(229,228,227);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1228.3,712.716L1177.25,821.977C1187.76,836.671 1198.67,850.77 1209,865.713C1219.45,874.636 1226.47,894.227 1208.18,869.09C1199.88,869.404 1154.72,870.247 1149.17,868.342C1130.51,858.443 1107.39,843.86 1088.66,832.959C1099.14,826.626 1117.98,817.48 1126.89,811.044C1136.44,804.133 1154.5,785.529 1163.71,776.506C1185.38,755.383 1206.91,734.12 1228.3,712.716Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1209,865.713C1219.45,874.636 1226.47,894.227 1208.18,869.09C1199.88,869.404 1154.72,870.247 1149.17,868.342C1158.75,867.532 1205.19,870.377 1209,865.713Z" style="fill:rgb(52,50,51);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1333.39,713.392C1355.47,734.234 1377.91,756.725 1399.46,778.251C1425.23,804.008 1438.73,816.721 1472.12,832.344C1459.85,839.726 1419.58,862.166 1410.8,869.15C1397.53,869.311 1364.74,870.385 1352.74,869.192C1352.23,867.922 1351.89,867.068 1351.28,865.837C1362.05,851.206 1372.91,836.647 1383.87,822.161C1367.04,786.412 1349.28,749.469 1333.39,713.392Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1239.83,891.5C1271.44,890.865 1306.06,891.718 1337.84,892.013C1328,906.67 1318.61,921.864 1308.88,936.658C1304.91,936.732 1294.23,927.336 1284.14,926.618C1270.4,925.64 1262.97,929.46 1252.44,937.566C1242.42,924.036 1231.87,906.324 1222.26,891.847L1239.83,891.5Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1202.02,1241.87C1202.21,1241.04 1202.19,1240.46 1202.8,1239.98C1204.73,1239.69 1204.01,1248.97 1203.98,1250.27L1205.5,1284.24C1220.65,1301.02 1235.98,1317.63 1251.51,1334.06C1252.7,1341.96 1257,1356.91 1259.14,1365.16C1236.64,1351.28 1203.31,1326.48 1180.94,1310.91C1187.57,1287.58 1195.36,1265.02 1202.02,1241.87Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1202.02,1241.87C1202.21,1241.04 1202.19,1240.46 1202.8,1239.98C1204.73,1239.69 1204.01,1248.97 1203.98,1250.27C1203.33,1247.32 1202.84,1244.8 1202.02,1241.87Z" style="fill:rgb(130,128,128);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1356.86,1249.9C1356.84,1248.26 1356.41,1240.46 1357.66,1239.59C1359.06,1239.84 1359.37,1243.13 1359.63,1244.35C1366.65,1266.61 1373.49,1288.16 1379.59,1310.68C1355.02,1329.17 1326.3,1347.75 1300.87,1365.76C1304.13,1355.65 1307.39,1344.41 1309.37,1333.97C1323.65,1319 1342.48,1299.34 1355.63,1283.73C1356.07,1273.66 1357.07,1259.67 1356.86,1249.9Z" style="fill:rgb(25,23,24);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1356.86,1249.9C1356.84,1248.26 1356.41,1240.46 1357.66,1239.59C1359.06,1239.84 1359.37,1243.13 1359.63,1244.35C1357.36,1244.59 1357.5,1247.71 1356.86,1249.9Z" style="fill:rgb(105,102,103);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1228.78,1112.67C1234.41,1106.63 1239.26,1100.54 1245.16,1094.34L1234.2,1115.86C1231.74,1114.1 1231.48,1113.88 1228.78,1112.67Z" style="fill:rgb(52,50,51);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1351.28,865.837C1351.89,867.068 1352.23,867.922 1352.74,869.192C1350.09,871.683 1344.39,880.569 1342.97,880.88C1336.3,882.343 1350.41,866.703 1351.28,865.837Z" style="fill:rgb(52,50,51);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1432.83,656.866C1432.61,657.516 1431.42,662.192 1430.5,662.165C1429.39,660.699 1431.14,653.359 1431.54,651.473L1432.83,656.866Z" style="fill:rgb(105,102,103);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1129.5,654.246C1129.73,655.393 1131.07,660.919 1129.98,661.699C1128.43,661.495 1127.52,656.217 1127.21,654.902L1129.5,654.246Z" style="fill:rgb(105,102,103);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1,0,0,1,-506,-152)">
|
||||||
|
<path d="M1233.99,651.16C1238.94,648.18 1242.71,647.988 1236.23,653.085L1233.99,651.16Z" style="fill:rgb(52,50,51);fill-rule:nonzero;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
BIN
webseite-ts-claude/frontend/assets/img/header-image.webp
Normal file
BIN
webseite-ts-claude/frontend/assets/img/header-image.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
BIN
webseite-ts-claude/frontend/assets/img/slider/slide-01.webp
Normal file
BIN
webseite-ts-claude/frontend/assets/img/slider/slide-01.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
BIN
webseite-ts-claude/frontend/assets/img/slider/slide-02.webp
Normal file
BIN
webseite-ts-claude/frontend/assets/img/slider/slide-02.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
webseite-ts-claude/frontend/assets/img/slider/slide-03.webp
Normal file
BIN
webseite-ts-claude/frontend/assets/img/slider/slide-03.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
8586
webseite-ts-claude/frontend/assets/js/build.js
Normal file
8586
webseite-ts-claude/frontend/assets/js/build.js
Normal file
File diff suppressed because it is too large
Load Diff
7
webseite-ts-claude/frontend/assets/js/build.js.map
Normal file
7
webseite-ts-claude/frontend/assets/js/build.js.map
Normal file
File diff suppressed because one or more lines are too long
5
webseite-ts-claude/frontend/data/products.json
Normal file
5
webseite-ts-claude/frontend/data/products.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{ "name": "3Doodler 3D Printing Pen", "price": 29.99 },
|
||||||
|
{ "name": "Powerstation 5- E. Maximus Chargus", "price": 44.95 },
|
||||||
|
{ "name": "8-Bit Legendary Hero Heat-Change Mug", "price": 6.99 }
|
||||||
|
]
|
||||||
49
webseite-ts-claude/frontend/dev/scripts/main.js
Normal file
49
webseite-ts-claude/frontend/dev/scripts/main.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// import 'bootstrap'; // ohne Pfadangabe (Modul auslesen aus node_modules) funktioniert nur mit JS-Bundler (esbuild, rollup & co.)
|
||||||
|
|
||||||
|
import ProductManager from './modules/ProductManager'; // Dateiendung kann weggelassen werden, wenn ein Bundler eingesetzt wird (esbuild, webpack, rollup, etc.)
|
||||||
|
import Splide from '@splidejs/splide';
|
||||||
|
|
||||||
|
(() => {
|
||||||
|
// === DOM & VARS =======
|
||||||
|
const DOM = {};
|
||||||
|
|
||||||
|
// === INIT =============
|
||||||
|
const init = () => {
|
||||||
|
console.log('init');
|
||||||
|
router();
|
||||||
|
};
|
||||||
|
|
||||||
|
// === EVENTHANDLER =====
|
||||||
|
|
||||||
|
// === XHR/FETCH ========
|
||||||
|
|
||||||
|
// === FUNCTIONS ========
|
||||||
|
const router = () => {
|
||||||
|
const path = window.location.pathname;
|
||||||
|
|
||||||
|
console.log(path);
|
||||||
|
|
||||||
|
switch (path) {
|
||||||
|
case '/':
|
||||||
|
case '/index.html':
|
||||||
|
console.log('HOME');
|
||||||
|
const splider = new Splide('.splide', {
|
||||||
|
type: 'loop',
|
||||||
|
fixedHeight: '100%',
|
||||||
|
height: '100%',
|
||||||
|
autoHeight: true,
|
||||||
|
}).mount();
|
||||||
|
|
||||||
|
// nur Skripte, die für Home relevant ausführen
|
||||||
|
break;
|
||||||
|
case '/projects/product-manager.html':
|
||||||
|
console.log('PRODUCT_MANAGER');
|
||||||
|
const pm = ProductManager();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn('Page not found');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
init();
|
||||||
|
})();
|
||||||
@@ -0,0 +1,574 @@
|
|||||||
|
import { Modal } from 'bootstrap'; // ohne Pfadangabe (Modul auslesen aus node_modules) funktioniert nur mit JS-Bundler (esbuild, rollup & co.)
|
||||||
|
import Toast from './Toast';
|
||||||
|
|
||||||
|
// import * as bootstrap from 'bootstrap';
|
||||||
|
// import 'bootstrap';
|
||||||
|
|
||||||
|
// import 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js'; // würde auch ohne Bundler funktionieren
|
||||||
|
// import '../../../node_modules/bootstrap/dist/js/bootstrap.esm.js'; // würde auch ohne Bundler funktionieren
|
||||||
|
|
||||||
|
// Factory Function
|
||||||
|
const ProductManager = (el = null) => {
|
||||||
|
// === DOM & VARS =======
|
||||||
|
|
||||||
|
const BASE_URL = 'http://127.0.0.1:8000';
|
||||||
|
|
||||||
|
const module = el || document.querySelector('.product-manager') || console.error('Product Manager not found');
|
||||||
|
|
||||||
|
if (!module) return;
|
||||||
|
|
||||||
|
const DOM = {
|
||||||
|
module,
|
||||||
|
table: module.querySelector('.table-products'),
|
||||||
|
tBody: module.querySelector('tbody'),
|
||||||
|
inputName: module.querySelector('.input-product-name'),
|
||||||
|
inputPrice: module.querySelector('.input-product-price'),
|
||||||
|
btnAdd: module.querySelector('.button-product-add'),
|
||||||
|
templateRow: module.querySelector('.template-row'),
|
||||||
|
|
||||||
|
// Nav Actions
|
||||||
|
btnSave: module.querySelector('.button-products-save'),
|
||||||
|
btnReset: module.querySelector('.button-products-reset'),
|
||||||
|
|
||||||
|
// Modal Edit
|
||||||
|
modalEdit: module.querySelector('.modal-edit'),
|
||||||
|
formEdit: module.querySelector('.form-product-edit'),
|
||||||
|
inputEditName: module.querySelector('.input-edit-name'),
|
||||||
|
inputEditPrice: module.querySelector('.input-edit-price'),
|
||||||
|
inputEditId: module.querySelector('.input-edit-id'),
|
||||||
|
inputEditPosition: module.querySelector('.input-edit-position'),
|
||||||
|
btnUpdate: module.querySelector('.button-product-update'),
|
||||||
|
|
||||||
|
// Modal Delete
|
||||||
|
modalDelete: module.querySelector('.modal-delete'),
|
||||||
|
productName: module.querySelector('strong.product-name'),
|
||||||
|
btnConfirmDelete: module.querySelector('.button-confirm-delete'),
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(DOM);
|
||||||
|
const bsModalEdit = new Modal(DOM.modalEdit, {
|
||||||
|
backdrop: 'static', // true
|
||||||
|
keyboard: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const bsModalDelete = new Modal(DOM.modalDelete, {
|
||||||
|
backdrop: 'static', // true
|
||||||
|
keyboard: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// === INIT =============
|
||||||
|
const init = () => {
|
||||||
|
console.log('init');
|
||||||
|
// Funktionaufrufe zu beginn der Anwendung
|
||||||
|
initProducts();
|
||||||
|
|
||||||
|
// Event-Lauscher zu beginn der Anwendung
|
||||||
|
DOM.btnReset.addEventListener('click', onClickReset);
|
||||||
|
DOM.btnSave.addEventListener('click', onClickSave);
|
||||||
|
|
||||||
|
DOM.btnAdd.addEventListener('click', onClickAdd);
|
||||||
|
DOM.btnAdd.disabled = true;
|
||||||
|
DOM.btnConfirmDelete.addEventListener('click', onClickConfirmDelete);
|
||||||
|
DOM.formEdit.addEventListener('submit', onSubmitEdit);
|
||||||
|
window.addEventListener('keyup', onKeyUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
// === EVENTHANDLER =====
|
||||||
|
const onKeyUp = (e) => {
|
||||||
|
// if (DOM.inputName.value === '' && DOM.inputPrice.value === '') {
|
||||||
|
// DOM.btnAdd.disabled = true;
|
||||||
|
// } else {
|
||||||
|
// DOM.btnAdd.disabled = false;
|
||||||
|
// }
|
||||||
|
DOM.btnAdd.disabled = (DOM.inputName.value === '' || DOM.inputPrice.value === ''); // prettier-ignore
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickReset = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
|
||||||
|
// async process
|
||||||
|
resetProducts().then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
Toast(data.msg, 'success').show();
|
||||||
|
loadProducts();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickSave = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
const icon = btnEl.querySelector('i');
|
||||||
|
|
||||||
|
icon.classList.add('fa-jello');
|
||||||
|
|
||||||
|
// async process
|
||||||
|
saveProducts().then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
setTimeout(() => {
|
||||||
|
icon.classList.remove('fa-jello');
|
||||||
|
}, 500);
|
||||||
|
Toast(data.msg, 'success').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmitEdit = (e) => {
|
||||||
|
e.preventDefault(); // Standardverhalten unterbinden (Formular nicht an action versenden)
|
||||||
|
|
||||||
|
// console.log(Object.entries(new FormData(DOM.formEdit)));
|
||||||
|
|
||||||
|
const product = {
|
||||||
|
name: DOM.inputEditName.value,
|
||||||
|
price: Number(DOM.inputEditPrice.value),
|
||||||
|
_id: DOM.inputEditId.value,
|
||||||
|
position: DOM.inputEditPosition.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
// update async process
|
||||||
|
DOM.btnUpdate.querySelector('i').classList.add('fa-spin');
|
||||||
|
updateProduct(product).then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
if (data.success) {
|
||||||
|
Toast(data.msg, 'success').show();
|
||||||
|
DOM.btnUpdate.querySelector('i').classList.remove('fa-spin');
|
||||||
|
loadProducts(); // All Produkte auslesen
|
||||||
|
resetFields();
|
||||||
|
bsModalEdit.hide();
|
||||||
|
} else {
|
||||||
|
Toast(data.msg, 'error').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickAdd = (e) => {
|
||||||
|
console.log('click');
|
||||||
|
|
||||||
|
const product = {
|
||||||
|
name: DOM.inputName.value,
|
||||||
|
price: Number(DOM.inputPrice.value),
|
||||||
|
};
|
||||||
|
|
||||||
|
createProduct(product).then((data) => {
|
||||||
|
console.log(data);
|
||||||
|
if (data.success) {
|
||||||
|
console.log('add');
|
||||||
|
Toast(data.msg, 'success').show();
|
||||||
|
loadProducts();
|
||||||
|
resetFields();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// addProduct(product);
|
||||||
|
|
||||||
|
disableNonFunctionalButtons();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickEdit = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
const currentRow = parents(btnEl, 'tr')[0];
|
||||||
|
const id = currentRow.dataset.id;
|
||||||
|
|
||||||
|
// async fetch
|
||||||
|
getProduct(id).then((data) => {
|
||||||
|
// console.log(data);
|
||||||
|
showModalEdit(data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickRemove = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
const currentRow = parents(btnEl, 'tr')[0];
|
||||||
|
const id = currentRow.dataset.id;
|
||||||
|
|
||||||
|
const productName = currentRow.querySelector('.td-name').textContent.trim();
|
||||||
|
|
||||||
|
DOM.btnConfirmDelete.dataset.id = id;
|
||||||
|
DOM.productName.textContent = productName;
|
||||||
|
|
||||||
|
// async fetch
|
||||||
|
// deleteProduct(id).then((data) => {
|
||||||
|
// if (data.success) {
|
||||||
|
// Toast(data.msg, 'success').show();
|
||||||
|
// console.log('delete: success', data);
|
||||||
|
// loadProducts();
|
||||||
|
// } else {
|
||||||
|
// Toast(data.msg, 'error').show();
|
||||||
|
// console.error(data);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickConfirmDelete = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
const id = btnEl.dataset.id;
|
||||||
|
|
||||||
|
// async fetch
|
||||||
|
deleteProduct(id).then((data) => {
|
||||||
|
if (data.success) {
|
||||||
|
Toast(data.msg, 'success').show();
|
||||||
|
bsModalDelete.hide();
|
||||||
|
loadProducts();
|
||||||
|
} else {
|
||||||
|
Toast(data.msg, 'error').show();
|
||||||
|
console.error(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickMoveUp = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
// const currentRowEl = btnEl.parentNode.parentNode; // aktuelle Zeile (tr) mit dem Button
|
||||||
|
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||||
|
|
||||||
|
// ParentNode.insertBefore(referenceElement, targetElement)
|
||||||
|
// DOM.tBody.insertBefore(currentRowEl, currentRowEl.previousElementSibling);
|
||||||
|
|
||||||
|
// targetElement.before(referenceElement)
|
||||||
|
currentRowEl.previousElementSibling.before(currentRowEl);
|
||||||
|
|
||||||
|
console.log('move up');
|
||||||
|
disableNonFunctionalButtons();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickMoveDown = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
// const currentRowEl = btnEl.parentNode.parentNode; // aktuelle Zeile (tr) mit dem Button
|
||||||
|
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||||
|
|
||||||
|
// ParentNode.insertBefore(referenceElement, targetElement)
|
||||||
|
// DOM.tBody.insertBefore(currentRowEl, currentRowEl.nextElementSibling.nextElementSibling);
|
||||||
|
|
||||||
|
// targetElement.after(referenceElement)
|
||||||
|
currentRowEl.nextElementSibling.after(currentRowEl);
|
||||||
|
|
||||||
|
console.log('move down');
|
||||||
|
disableNonFunctionalButtons();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Drag 'n Drop EventHandler
|
||||||
|
const onMouseDownDrag = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||||
|
currentRowEl.draggable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUpDrag = (e) => {
|
||||||
|
const btnEl = e.currentTarget;
|
||||||
|
const currentRowEl = parents(btnEl, 'tr')[0];
|
||||||
|
currentRowEl.draggable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragStart = (e) => {
|
||||||
|
console.log('DragEvent: ', e);
|
||||||
|
const currentRowEl = e.currentTarget; // tr <- aktuelle Zeie, die bewegt wird
|
||||||
|
const idx = [...currentRowEl.parentNode.children].indexOf(currentRowEl);
|
||||||
|
|
||||||
|
e.dataTransfer.setData('text/plain', idx);
|
||||||
|
// e.dataTransfer.setData('application/json', JSON.stringify({idx, name: sourceElement}));
|
||||||
|
console.log('tr index:', idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragOver = (e) => {
|
||||||
|
e.preventDefault(); // WICHTIG! Sonst funktioniert drop EventHandler nicht.
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDrop = (e) => {
|
||||||
|
const currentRowEl = e.currentTarget; // tr <- Zielzeile, auf die gedroppt wird.
|
||||||
|
const targetIdx = [...currentRowEl.parentNode.children].indexOf(currentRowEl);
|
||||||
|
const sourceIdx = parseInt(e.dataTransfer.getData('text/plain'));
|
||||||
|
// const { sourceIdx } = JSON.parse(e.dataTransfer.getData('application/json'));
|
||||||
|
|
||||||
|
console.log({ targetIdx, sourceIdx });
|
||||||
|
|
||||||
|
// Guard wenn Element auf sich selbst losgelassen wurde, abbruch
|
||||||
|
if (sourceIdx === targetIdx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const draggedEl = DOM.tBody.querySelector(`tr:nth-child(${sourceIdx + 1})`);
|
||||||
|
// const draggedEl = DOM.tBody.children[sourceIdx]
|
||||||
|
|
||||||
|
// Verschiebe das Element im DOM
|
||||||
|
if (sourceIdx > targetIdx) {
|
||||||
|
currentRowEl.before(draggedEl);
|
||||||
|
} else {
|
||||||
|
currentRowEl.after(draggedEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
disableNonFunctionalButtons();
|
||||||
|
};
|
||||||
|
|
||||||
|
// === XHR/FETCH ========
|
||||||
|
|
||||||
|
// HTTP - Methode - PATCH
|
||||||
|
// reset products
|
||||||
|
const resetProducts = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BASE_URL}/api/products/reset`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ reset: true }),
|
||||||
|
}); // HTTP Request URL
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fetch went wrong.');
|
||||||
|
}
|
||||||
|
const data = await response.json(); // HTTP-Response
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
Toast(error, 'error').show();
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HTTP - Methode - PATCH
|
||||||
|
// save products
|
||||||
|
const saveProducts = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BASE_URL}/api/products/save`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ save: true }),
|
||||||
|
}); // HTTP Request URL
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fetch went wrong.');
|
||||||
|
}
|
||||||
|
const data = await response.json(); // HTTP-Response
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
Toast(error, 'error').show();
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HTTP - Methode - POST
|
||||||
|
// (C)RUD - create
|
||||||
|
// BRE(A)D - add - neues Produkt hinzufügen
|
||||||
|
const createProduct = async (product) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BASE_URL}/api/products`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(product),
|
||||||
|
}); // HTTP Request URL
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fetch went wrong.');
|
||||||
|
}
|
||||||
|
const data = await response.json(); // HTTP-Response
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
Toast(error, 'error').show();
|
||||||
|
|
||||||
|
console.error(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HTTP - Methode - GET
|
||||||
|
// C(R)UD - Read
|
||||||
|
// (B)READ - Browse - auslesen aller Produkte
|
||||||
|
const getProducts = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BASE_URL}/api/products`); // HTTP Request URL
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fetch went wrong.');
|
||||||
|
}
|
||||||
|
const data = await response.json(); // HTTP-Response
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HTTP - Methode - GET
|
||||||
|
// C(R)UD - Read
|
||||||
|
// B(R)EAD - Read - auslesen eines Produkts
|
||||||
|
const getProduct = async (id) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BASE_URL}/api/products/${id}`); // HTTP Request URL
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fetch went wrong.');
|
||||||
|
}
|
||||||
|
const data = await response.json(); // HTTP-Response
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HTTP - Methode - PUT
|
||||||
|
// CR(U)D - update
|
||||||
|
// BR(E)AD - edit - Produkt aktualisieren
|
||||||
|
const updateProduct = async (product) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BASE_URL}/api/products`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(product),
|
||||||
|
}); // HTTP Request URL
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fetch went wrong.');
|
||||||
|
}
|
||||||
|
const data = await response.json(); // HTTP-Response
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HTTP - Methode - DELETE
|
||||||
|
// CRU(D) - delete
|
||||||
|
// BREA(D) - delete - Produkt löschen
|
||||||
|
const deleteProduct = async (id) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BASE_URL}/api/products/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
}); // HTTP Request URL
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fetch went wrong.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// === FUNCTIONS ========
|
||||||
|
|
||||||
|
const initProducts = () => {
|
||||||
|
getProducts().then((products) => {
|
||||||
|
products.forEach((product) => {
|
||||||
|
addProduct(product);
|
||||||
|
});
|
||||||
|
Toast('Init products').show();
|
||||||
|
disableNonFunctionalButtons();
|
||||||
|
});
|
||||||
|
|
||||||
|
// PRODUCTS.forEach((product) => {
|
||||||
|
// addProduct(product);
|
||||||
|
// });
|
||||||
|
//disableNonFunctionalButtons();
|
||||||
|
// PRODUCTS.forEach(addProduct);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadProducts = () => {
|
||||||
|
DOM.tBody.innerHTML = '';
|
||||||
|
getProducts().then((data) => {
|
||||||
|
data.forEach((product) => {
|
||||||
|
addProduct(product);
|
||||||
|
});
|
||||||
|
disableNonFunctionalButtons();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addProduct = (product) => {
|
||||||
|
const { name, price, _id: id, position } = product;
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/de/docs/Web/API/HTMLTemplateElement/content
|
||||||
|
const trEl = DOM.templateRow.content.firstElementChild.cloneNode(true);
|
||||||
|
|
||||||
|
const tdNameEl = trEl.querySelector('.td-name');
|
||||||
|
const tdPriceEl = trEl.querySelector('.td-price');
|
||||||
|
const btnRemoveEl = trEl.querySelector('.button-product-remove');
|
||||||
|
const btnMoveUpEl = trEl.querySelector('.button-product-move-up');
|
||||||
|
const btnMoveDownEl = trEl.querySelector('.button-product-move-down');
|
||||||
|
|
||||||
|
const btnDragEl = trEl.querySelector('.button-drag');
|
||||||
|
const btnEditEl = trEl.querySelector('.button-product-edit');
|
||||||
|
|
||||||
|
tdNameEl.textContent = name;
|
||||||
|
tdPriceEl.textContent = price;
|
||||||
|
|
||||||
|
btnEditEl.addEventListener('click', onClickEdit);
|
||||||
|
btnRemoveEl.addEventListener('click', onClickRemove);
|
||||||
|
btnMoveUpEl.addEventListener('click', onClickMoveUp);
|
||||||
|
btnMoveDownEl.addEventListener('click', onClickMoveDown);
|
||||||
|
|
||||||
|
// Drag 'n Drop Events
|
||||||
|
btnDragEl.addEventListener('mousedown', onMouseDownDrag);
|
||||||
|
btnDragEl.addEventListener('mouseup', onMouseUpDrag);
|
||||||
|
|
||||||
|
trEl.addEventListener('dragstart', onDragStart);
|
||||||
|
trEl.addEventListener('dragover', onDragOver);
|
||||||
|
trEl.addEventListener('drop', onDrop);
|
||||||
|
|
||||||
|
trEl.dataset.id = id; // <tr data-id="..." >...</tr>
|
||||||
|
trEl.dataset.position = position; // <tr data-position="..." >...</tr>
|
||||||
|
|
||||||
|
DOM.tBody.appendChild(trEl); // Im DOM hinzufügen
|
||||||
|
};
|
||||||
|
|
||||||
|
const showModalEdit = (product) => {
|
||||||
|
const { name = '', price = 0, _id: id, position } = product;
|
||||||
|
|
||||||
|
DOM.inputEditName.value = name;
|
||||||
|
DOM.inputEditPrice.value = price;
|
||||||
|
|
||||||
|
DOM.inputEditId.value = id;
|
||||||
|
DOM.inputEditPosition.value = position;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetFields = () => {
|
||||||
|
DOM.btnAdd.disabled = true;
|
||||||
|
DOM.inputPrice.value = '';
|
||||||
|
DOM.inputName.value = '';
|
||||||
|
|
||||||
|
DOM.formEdit.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
const disableNonFunctionalButtons = () => {
|
||||||
|
Array.from(DOM.tBody.querySelectorAll('tr')).forEach((tr) => {
|
||||||
|
const btnMoveUp = tr.querySelector('.button-product-move-up');
|
||||||
|
const btnMoveDown = tr.querySelector('.button-product-move-down');
|
||||||
|
|
||||||
|
btnMoveUp.disabled = !tr.previousElementSibling;
|
||||||
|
btnMoveDown.disabled = !tr.nextElementSibling;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helferfunktion aus jQuery (youmightnotjquery)
|
||||||
|
const parents = (el, selector) => {
|
||||||
|
const parents = [];
|
||||||
|
while ((el = el.parentNode) && el !== document) {
|
||||||
|
if (!selector || el.matches(selector)) parents.push(el);
|
||||||
|
}
|
||||||
|
return parents;
|
||||||
|
};
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
return {
|
||||||
|
init,
|
||||||
|
initProducts,
|
||||||
|
resetFields,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Freigabe für import
|
||||||
|
export default ProductManager;
|
||||||
59
webseite-ts-claude/frontend/dev/scripts/modules/Toast.js
Normal file
59
webseite-ts-claude/frontend/dev/scripts/modules/Toast.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import Toastify from 'toastify-js'; // das Modul wird von node_modules ausgelesen und gebundlet. Funktioniert nur mit JS-Bundler
|
||||||
|
|
||||||
|
const Toast = (text = '', variant = 'primary') => {
|
||||||
|
const getColorByName = (variant = '') => {
|
||||||
|
let color;
|
||||||
|
switch (variant.trim().toLowerCase()) {
|
||||||
|
case 'primary':
|
||||||
|
color = '#0d6efd';
|
||||||
|
break;
|
||||||
|
case 'secondary':
|
||||||
|
color = '#6c757d';
|
||||||
|
break;
|
||||||
|
case 'warning':
|
||||||
|
color = '#ffc720';
|
||||||
|
break;
|
||||||
|
case 'success':
|
||||||
|
color = '#13653f';
|
||||||
|
break;
|
||||||
|
case 'info':
|
||||||
|
color = '#25cff2';
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
case 'danger':
|
||||||
|
color = '#a52834';
|
||||||
|
break;
|
||||||
|
case 'light':
|
||||||
|
color = '#babbbc';
|
||||||
|
break;
|
||||||
|
case 'dark':
|
||||||
|
color = '#373b3e';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn('variant not found');
|
||||||
|
color: 'white';
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
Toastify({
|
||||||
|
text: text,
|
||||||
|
duration: 2000,
|
||||||
|
className: variant,
|
||||||
|
gravity: 'top', // `top` or `bottom`
|
||||||
|
position: 'center', // `left`, `center` or `right`
|
||||||
|
stopOnFocus: true, // Prevents dismissing of toast on hover
|
||||||
|
style: {
|
||||||
|
background: getColorByName(variant),
|
||||||
|
},
|
||||||
|
}).showToast();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
show,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Toast;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// @import 'bootstrap-5.3.8/bootstrap-grid';
|
||||||
|
@import 'bootstrap-5.3.8/bootstrap';
|
||||||
|
// @import 'bulma-1.04/index'
|
||||||
153
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_accordion.scss
vendored
Normal file
153
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_accordion.scss
vendored
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
//
|
||||||
|
// Base styles
|
||||||
|
//
|
||||||
|
|
||||||
|
.accordion {
|
||||||
|
// scss-docs-start accordion-css-vars
|
||||||
|
--#{$prefix}accordion-color: #{$accordion-color};
|
||||||
|
--#{$prefix}accordion-bg: #{$accordion-bg};
|
||||||
|
--#{$prefix}accordion-transition: #{$accordion-transition};
|
||||||
|
--#{$prefix}accordion-border-color: #{$accordion-border-color};
|
||||||
|
--#{$prefix}accordion-border-width: #{$accordion-border-width};
|
||||||
|
--#{$prefix}accordion-border-radius: #{$accordion-border-radius};
|
||||||
|
--#{$prefix}accordion-inner-border-radius: #{$accordion-inner-border-radius};
|
||||||
|
--#{$prefix}accordion-btn-padding-x: #{$accordion-button-padding-x};
|
||||||
|
--#{$prefix}accordion-btn-padding-y: #{$accordion-button-padding-y};
|
||||||
|
--#{$prefix}accordion-btn-color: #{$accordion-button-color};
|
||||||
|
--#{$prefix}accordion-btn-bg: #{$accordion-button-bg};
|
||||||
|
--#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon)};
|
||||||
|
--#{$prefix}accordion-btn-icon-width: #{$accordion-icon-width};
|
||||||
|
--#{$prefix}accordion-btn-icon-transform: #{$accordion-icon-transform};
|
||||||
|
--#{$prefix}accordion-btn-icon-transition: #{$accordion-icon-transition};
|
||||||
|
--#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon)};
|
||||||
|
--#{$prefix}accordion-btn-focus-box-shadow: #{$accordion-button-focus-box-shadow};
|
||||||
|
--#{$prefix}accordion-body-padding-x: #{$accordion-body-padding-x};
|
||||||
|
--#{$prefix}accordion-body-padding-y: #{$accordion-body-padding-y};
|
||||||
|
--#{$prefix}accordion-active-color: #{$accordion-button-active-color};
|
||||||
|
--#{$prefix}accordion-active-bg: #{$accordion-button-active-bg};
|
||||||
|
// scss-docs-end accordion-css-vars
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-button {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--#{$prefix}accordion-btn-padding-y) var(--#{$prefix}accordion-btn-padding-x);
|
||||||
|
@include font-size($font-size-base);
|
||||||
|
color: var(--#{$prefix}accordion-btn-color);
|
||||||
|
text-align: left; // Reset button style
|
||||||
|
background-color: var(--#{$prefix}accordion-btn-bg);
|
||||||
|
border: 0;
|
||||||
|
@include border-radius(0);
|
||||||
|
overflow-anchor: none;
|
||||||
|
@include transition(var(--#{$prefix}accordion-transition));
|
||||||
|
|
||||||
|
&:not(.collapsed) {
|
||||||
|
color: var(--#{$prefix}accordion-active-color);
|
||||||
|
background-color: var(--#{$prefix}accordion-active-bg);
|
||||||
|
box-shadow: inset 0 calc(-1 * var(--#{$prefix}accordion-border-width)) 0 var(--#{$prefix}accordion-border-color); // stylelint-disable-line function-disallowed-list
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-image: var(--#{$prefix}accordion-btn-active-icon);
|
||||||
|
transform: var(--#{$prefix}accordion-btn-icon-transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accordion icon
|
||||||
|
&::after {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: var(--#{$prefix}accordion-btn-icon-width);
|
||||||
|
height: var(--#{$prefix}accordion-btn-icon-width);
|
||||||
|
margin-left: auto;
|
||||||
|
content: "";
|
||||||
|
background-image: var(--#{$prefix}accordion-btn-icon);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: var(--#{$prefix}accordion-btn-icon-width);
|
||||||
|
@include transition(var(--#{$prefix}accordion-btn-icon-transition));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
z-index: 3;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: var(--#{$prefix}accordion-btn-focus-box-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-item {
|
||||||
|
color: var(--#{$prefix}accordion-color);
|
||||||
|
background-color: var(--#{$prefix}accordion-bg);
|
||||||
|
border: var(--#{$prefix}accordion-border-width) solid var(--#{$prefix}accordion-border-color);
|
||||||
|
|
||||||
|
&:first-of-type {
|
||||||
|
@include border-top-radius(var(--#{$prefix}accordion-border-radius));
|
||||||
|
|
||||||
|
> .accordion-header .accordion-button {
|
||||||
|
@include border-top-radius(var(--#{$prefix}accordion-inner-border-radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set a border-radius on the last item if the accordion is collapsed
|
||||||
|
&:last-of-type {
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}accordion-border-radius));
|
||||||
|
|
||||||
|
> .accordion-header .accordion-button {
|
||||||
|
&.collapsed {
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}accordion-inner-border-radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .accordion-collapse {
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}accordion-border-radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-body {
|
||||||
|
padding: var(--#{$prefix}accordion-body-padding-y) var(--#{$prefix}accordion-body-padding-x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Flush accordion items
|
||||||
|
//
|
||||||
|
// Remove borders and border-radius to keep accordion items edge-to-edge.
|
||||||
|
|
||||||
|
.accordion-flush {
|
||||||
|
> .accordion-item {
|
||||||
|
border-right: 0;
|
||||||
|
border-left: 0;
|
||||||
|
@include border-radius(0);
|
||||||
|
|
||||||
|
&:first-child { border-top: 0; }
|
||||||
|
&:last-child { border-bottom: 0; }
|
||||||
|
|
||||||
|
// stylelint-disable selector-max-class
|
||||||
|
> .accordion-collapse,
|
||||||
|
> .accordion-header .accordion-button,
|
||||||
|
> .accordion-header .accordion-button.collapsed {
|
||||||
|
@include border-radius(0);
|
||||||
|
}
|
||||||
|
// stylelint-enable selector-max-class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
@include color-mode(dark) {
|
||||||
|
.accordion-button::after {
|
||||||
|
--#{$prefix}accordion-btn-icon: #{escape-svg($accordion-button-icon-dark)};
|
||||||
|
--#{$prefix}accordion-btn-active-icon: #{escape-svg($accordion-button-active-icon-dark)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_alert.scss
vendored
Normal file
68
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_alert.scss
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// Base styles
|
||||||
|
//
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
// scss-docs-start alert-css-vars
|
||||||
|
--#{$prefix}alert-bg: transparent;
|
||||||
|
--#{$prefix}alert-padding-x: #{$alert-padding-x};
|
||||||
|
--#{$prefix}alert-padding-y: #{$alert-padding-y};
|
||||||
|
--#{$prefix}alert-margin-bottom: #{$alert-margin-bottom};
|
||||||
|
--#{$prefix}alert-color: inherit;
|
||||||
|
--#{$prefix}alert-border-color: transparent;
|
||||||
|
--#{$prefix}alert-border: #{$alert-border-width} solid var(--#{$prefix}alert-border-color);
|
||||||
|
--#{$prefix}alert-border-radius: #{$alert-border-radius};
|
||||||
|
--#{$prefix}alert-link-color: inherit;
|
||||||
|
// scss-docs-end alert-css-vars
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
padding: var(--#{$prefix}alert-padding-y) var(--#{$prefix}alert-padding-x);
|
||||||
|
margin-bottom: var(--#{$prefix}alert-margin-bottom);
|
||||||
|
color: var(--#{$prefix}alert-color);
|
||||||
|
background-color: var(--#{$prefix}alert-bg);
|
||||||
|
border: var(--#{$prefix}alert-border);
|
||||||
|
@include border-radius(var(--#{$prefix}alert-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headings for larger alerts
|
||||||
|
.alert-heading {
|
||||||
|
// Specified to prevent conflicts of changing $headings-color
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide class for links that match alerts
|
||||||
|
.alert-link {
|
||||||
|
font-weight: $alert-link-font-weight;
|
||||||
|
color: var(--#{$prefix}alert-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Dismissible alerts
|
||||||
|
//
|
||||||
|
// Expand the right padding and account for the close button's positioning.
|
||||||
|
|
||||||
|
.alert-dismissible {
|
||||||
|
padding-right: $alert-dismissible-padding-r;
|
||||||
|
|
||||||
|
// Adjust close link position
|
||||||
|
.btn-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: $stretched-link-z-index + 1;
|
||||||
|
padding: $alert-padding-y * 1.25 $alert-padding-x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// scss-docs-start alert-modifiers
|
||||||
|
// Generate contextual modifier classes for colorizing the alert
|
||||||
|
@each $state in map-keys($theme-colors) {
|
||||||
|
.alert-#{$state} {
|
||||||
|
--#{$prefix}alert-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
|
--#{$prefix}alert-bg: var(--#{$prefix}#{$state}-bg-subtle);
|
||||||
|
--#{$prefix}alert-border-color: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}alert-link-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scss-docs-end alert-modifiers
|
||||||
38
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_badge.scss
vendored
Normal file
38
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_badge.scss
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Base class
|
||||||
|
//
|
||||||
|
// Requires one of the contextual, color modifier classes for `color` and
|
||||||
|
// `background-color`.
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
// scss-docs-start badge-css-vars
|
||||||
|
--#{$prefix}badge-padding-x: #{$badge-padding-x};
|
||||||
|
--#{$prefix}badge-padding-y: #{$badge-padding-y};
|
||||||
|
@include rfs($badge-font-size, --#{$prefix}badge-font-size);
|
||||||
|
--#{$prefix}badge-font-weight: #{$badge-font-weight};
|
||||||
|
--#{$prefix}badge-color: #{$badge-color};
|
||||||
|
--#{$prefix}badge-border-radius: #{$badge-border-radius};
|
||||||
|
// scss-docs-end badge-css-vars
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
padding: var(--#{$prefix}badge-padding-y) var(--#{$prefix}badge-padding-x);
|
||||||
|
@include font-size(var(--#{$prefix}badge-font-size));
|
||||||
|
font-weight: var(--#{$prefix}badge-font-weight);
|
||||||
|
line-height: 1;
|
||||||
|
color: var(--#{$prefix}badge-color);
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: baseline;
|
||||||
|
@include border-radius(var(--#{$prefix}badge-border-radius));
|
||||||
|
@include gradient-bg();
|
||||||
|
|
||||||
|
// Empty badges collapse automatically
|
||||||
|
&:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick fix for badges in buttons
|
||||||
|
.btn .badge {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
40
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_breadcrumb.scss
vendored
Normal file
40
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_breadcrumb.scss
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
.breadcrumb {
|
||||||
|
// scss-docs-start breadcrumb-css-vars
|
||||||
|
--#{$prefix}breadcrumb-padding-x: #{$breadcrumb-padding-x};
|
||||||
|
--#{$prefix}breadcrumb-padding-y: #{$breadcrumb-padding-y};
|
||||||
|
--#{$prefix}breadcrumb-margin-bottom: #{$breadcrumb-margin-bottom};
|
||||||
|
@include rfs($breadcrumb-font-size, --#{$prefix}breadcrumb-font-size);
|
||||||
|
--#{$prefix}breadcrumb-bg: #{$breadcrumb-bg};
|
||||||
|
--#{$prefix}breadcrumb-border-radius: #{$breadcrumb-border-radius};
|
||||||
|
--#{$prefix}breadcrumb-divider-color: #{$breadcrumb-divider-color};
|
||||||
|
--#{$prefix}breadcrumb-item-padding-x: #{$breadcrumb-item-padding-x};
|
||||||
|
--#{$prefix}breadcrumb-item-active-color: #{$breadcrumb-active-color};
|
||||||
|
// scss-docs-end breadcrumb-css-vars
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: var(--#{$prefix}breadcrumb-padding-y) var(--#{$prefix}breadcrumb-padding-x);
|
||||||
|
margin-bottom: var(--#{$prefix}breadcrumb-margin-bottom);
|
||||||
|
@include font-size(var(--#{$prefix}breadcrumb-font-size));
|
||||||
|
list-style: none;
|
||||||
|
background-color: var(--#{$prefix}breadcrumb-bg);
|
||||||
|
@include border-radius(var(--#{$prefix}breadcrumb-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-item {
|
||||||
|
// The separator between breadcrumbs (by default, a forward-slash: "/")
|
||||||
|
+ .breadcrumb-item {
|
||||||
|
padding-left: var(--#{$prefix}breadcrumb-item-padding-x);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
float: left; // Suppress inline spacings and underlining of the separator
|
||||||
|
padding-right: var(--#{$prefix}breadcrumb-item-padding-x);
|
||||||
|
color: var(--#{$prefix}breadcrumb-divider-color);
|
||||||
|
content: var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: var(--#{$prefix}breadcrumb-item-active-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
147
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_button-group.scss
vendored
Normal file
147
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_button-group.scss
vendored
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
// Make the div behave like a button
|
||||||
|
.btn-group,
|
||||||
|
.btn-group-vertical {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
vertical-align: middle; // match .btn alignment given font-size hack above
|
||||||
|
|
||||||
|
> .btn {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring the hover, focused, and "active" buttons to the front to overlay
|
||||||
|
// the borders properly
|
||||||
|
> .btn-check:checked + .btn,
|
||||||
|
> .btn-check:focus + .btn,
|
||||||
|
> .btn:hover,
|
||||||
|
> .btn:focus,
|
||||||
|
> .btn:active,
|
||||||
|
> .btn.active {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Group multiple button groups together for a toolbar
|
||||||
|
.btn-toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
@include border-radius($btn-border-radius);
|
||||||
|
|
||||||
|
// Prevent double borders when buttons are next to each other
|
||||||
|
> :not(.btn-check:first-child) + .btn,
|
||||||
|
> .btn-group:not(:first-child) {
|
||||||
|
margin-left: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset rounded corners
|
||||||
|
> .btn:not(:last-child):not(.dropdown-toggle),
|
||||||
|
> .btn.dropdown-toggle-split:first-child,
|
||||||
|
> .btn-group:not(:last-child) > .btn {
|
||||||
|
@include border-end-radius(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The left radius should be 0 if the button is:
|
||||||
|
// - the "third or more" child
|
||||||
|
// - the second child and the previous element isn't `.btn-check` (making it the first child visually)
|
||||||
|
// - part of a btn-group which isn't the first child
|
||||||
|
> .btn:nth-child(n + 3),
|
||||||
|
> :not(.btn-check) + .btn,
|
||||||
|
> .btn-group:not(:first-child) > .btn {
|
||||||
|
@include border-start-radius(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizing
|
||||||
|
//
|
||||||
|
// Remix the default button sizing classes into new ones for easier manipulation.
|
||||||
|
|
||||||
|
.btn-group-sm > .btn { @extend .btn-sm; }
|
||||||
|
.btn-group-lg > .btn { @extend .btn-lg; }
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Split button dropdowns
|
||||||
|
//
|
||||||
|
|
||||||
|
.dropdown-toggle-split {
|
||||||
|
padding-right: $btn-padding-x * .75;
|
||||||
|
padding-left: $btn-padding-x * .75;
|
||||||
|
|
||||||
|
&::after,
|
||||||
|
.dropup &::after,
|
||||||
|
.dropend &::after {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropstart &::before {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm + .dropdown-toggle-split {
|
||||||
|
padding-right: $btn-padding-x-sm * .75;
|
||||||
|
padding-left: $btn-padding-x-sm * .75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lg + .dropdown-toggle-split {
|
||||||
|
padding-right: $btn-padding-x-lg * .75;
|
||||||
|
padding-left: $btn-padding-x-lg * .75;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The clickable button for toggling the menu
|
||||||
|
// Set the same inset shadow as the :active state
|
||||||
|
.btn-group.show .dropdown-toggle {
|
||||||
|
@include box-shadow($btn-active-box-shadow);
|
||||||
|
|
||||||
|
// Show no shadow for `.btn-link` since it has no other button styles.
|
||||||
|
&.btn-link {
|
||||||
|
@include box-shadow(none);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Vertical button groups
|
||||||
|
//
|
||||||
|
|
||||||
|
.btn-group-vertical {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
> .btn,
|
||||||
|
> .btn-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .btn:not(:first-child),
|
||||||
|
> .btn-group:not(:first-child) {
|
||||||
|
margin-top: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset rounded corners
|
||||||
|
> .btn:not(:last-child):not(.dropdown-toggle),
|
||||||
|
> .btn-group:not(:last-child) > .btn {
|
||||||
|
@include border-bottom-radius(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The top radius should be 0 if the button is:
|
||||||
|
// - the "third or more" child
|
||||||
|
// - the second child and the previous element isn't `.btn-check` (making it the first child visually)
|
||||||
|
// - part of a btn-group which isn't the first child
|
||||||
|
> .btn:nth-child(n + 3),
|
||||||
|
> :not(.btn-check) + .btn,
|
||||||
|
> .btn-group:not(:first-child) > .btn {
|
||||||
|
@include border-top-radius(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
216
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_buttons.scss
vendored
Normal file
216
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_buttons.scss
vendored
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
//
|
||||||
|
// Base styles
|
||||||
|
//
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
// scss-docs-start btn-css-vars
|
||||||
|
--#{$prefix}btn-padding-x: #{$btn-padding-x};
|
||||||
|
--#{$prefix}btn-padding-y: #{$btn-padding-y};
|
||||||
|
--#{$prefix}btn-font-family: #{$btn-font-family};
|
||||||
|
@include rfs($btn-font-size, --#{$prefix}btn-font-size);
|
||||||
|
--#{$prefix}btn-font-weight: #{$btn-font-weight};
|
||||||
|
--#{$prefix}btn-line-height: #{$btn-line-height};
|
||||||
|
--#{$prefix}btn-color: #{$btn-color};
|
||||||
|
--#{$prefix}btn-bg: transparent;
|
||||||
|
--#{$prefix}btn-border-width: #{$btn-border-width};
|
||||||
|
--#{$prefix}btn-border-color: transparent;
|
||||||
|
--#{$prefix}btn-border-radius: #{$btn-border-radius};
|
||||||
|
--#{$prefix}btn-hover-border-color: transparent;
|
||||||
|
--#{$prefix}btn-box-shadow: #{$btn-box-shadow};
|
||||||
|
--#{$prefix}btn-disabled-opacity: #{$btn-disabled-opacity};
|
||||||
|
--#{$prefix}btn-focus-box-shadow: 0 0 0 #{$btn-focus-width} rgba(var(--#{$prefix}btn-focus-shadow-rgb), .5);
|
||||||
|
// scss-docs-end btn-css-vars
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
padding: var(--#{$prefix}btn-padding-y) var(--#{$prefix}btn-padding-x);
|
||||||
|
font-family: var(--#{$prefix}btn-font-family);
|
||||||
|
@include font-size(var(--#{$prefix}btn-font-size));
|
||||||
|
font-weight: var(--#{$prefix}btn-font-weight);
|
||||||
|
line-height: var(--#{$prefix}btn-line-height);
|
||||||
|
color: var(--#{$prefix}btn-color);
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
|
white-space: $btn-white-space;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: if($enable-button-pointers, pointer, null);
|
||||||
|
user-select: none;
|
||||||
|
border: var(--#{$prefix}btn-border-width) solid var(--#{$prefix}btn-border-color);
|
||||||
|
@include border-radius(var(--#{$prefix}btn-border-radius));
|
||||||
|
@include gradient-bg(var(--#{$prefix}btn-bg));
|
||||||
|
@include box-shadow(var(--#{$prefix}btn-box-shadow));
|
||||||
|
@include transition($btn-transition);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--#{$prefix}btn-hover-color);
|
||||||
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
|
background-color: var(--#{$prefix}btn-hover-bg);
|
||||||
|
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-check + &:hover {
|
||||||
|
// override for the checkbox/radio buttons
|
||||||
|
color: var(--#{$prefix}btn-color);
|
||||||
|
background-color: var(--#{$prefix}btn-bg);
|
||||||
|
border-color: var(--#{$prefix}btn-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
color: var(--#{$prefix}btn-hover-color);
|
||||||
|
@include gradient-bg(var(--#{$prefix}btn-hover-bg));
|
||||||
|
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||||
|
outline: 0;
|
||||||
|
// Avoid using mixin so we can pass custom focus shadow properly
|
||||||
|
@if $enable-shadows {
|
||||||
|
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
} @else {
|
||||||
|
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-check:focus-visible + & {
|
||||||
|
border-color: var(--#{$prefix}btn-hover-border-color);
|
||||||
|
outline: 0;
|
||||||
|
// Avoid using mixin so we can pass custom focus shadow properly
|
||||||
|
@if $enable-shadows {
|
||||||
|
box-shadow: var(--#{$prefix}btn-box-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
} @else {
|
||||||
|
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-check:checked + &,
|
||||||
|
:not(.btn-check) + &:active,
|
||||||
|
&:first-child:active,
|
||||||
|
&.active,
|
||||||
|
&.show {
|
||||||
|
color: var(--#{$prefix}btn-active-color);
|
||||||
|
background-color: var(--#{$prefix}btn-active-bg);
|
||||||
|
// Remove CSS gradients if they're enabled
|
||||||
|
background-image: if($enable-gradients, none, null);
|
||||||
|
border-color: var(--#{$prefix}btn-active-border-color);
|
||||||
|
@include box-shadow(var(--#{$prefix}btn-active-shadow));
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
// Avoid using mixin so we can pass custom focus shadow properly
|
||||||
|
@if $enable-shadows {
|
||||||
|
box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
} @else {
|
||||||
|
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-check:checked:focus-visible + & {
|
||||||
|
// Avoid using mixin so we can pass custom focus shadow properly
|
||||||
|
@if $enable-shadows {
|
||||||
|
box-shadow: var(--#{$prefix}btn-active-shadow), var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
} @else {
|
||||||
|
box-shadow: var(--#{$prefix}btn-focus-box-shadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&.disabled,
|
||||||
|
fieldset:disabled & {
|
||||||
|
color: var(--#{$prefix}btn-disabled-color);
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: var(--#{$prefix}btn-disabled-bg);
|
||||||
|
background-image: if($enable-gradients, none, null);
|
||||||
|
border-color: var(--#{$prefix}btn-disabled-border-color);
|
||||||
|
opacity: var(--#{$prefix}btn-disabled-opacity);
|
||||||
|
@include box-shadow(none);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Alternate buttons
|
||||||
|
//
|
||||||
|
|
||||||
|
// scss-docs-start btn-variant-loops
|
||||||
|
@each $color, $value in $theme-colors {
|
||||||
|
.btn-#{$color} {
|
||||||
|
@if $color == "light" {
|
||||||
|
@include button-variant(
|
||||||
|
$value,
|
||||||
|
$value,
|
||||||
|
$hover-background: shade-color($value, $btn-hover-bg-shade-amount),
|
||||||
|
$hover-border: shade-color($value, $btn-hover-border-shade-amount),
|
||||||
|
$active-background: shade-color($value, $btn-active-bg-shade-amount),
|
||||||
|
$active-border: shade-color($value, $btn-active-border-shade-amount)
|
||||||
|
);
|
||||||
|
} @else if $color == "dark" {
|
||||||
|
@include button-variant(
|
||||||
|
$value,
|
||||||
|
$value,
|
||||||
|
$hover-background: tint-color($value, $btn-hover-bg-tint-amount),
|
||||||
|
$hover-border: tint-color($value, $btn-hover-border-tint-amount),
|
||||||
|
$active-background: tint-color($value, $btn-active-bg-tint-amount),
|
||||||
|
$active-border: tint-color($value, $btn-active-border-tint-amount)
|
||||||
|
);
|
||||||
|
} @else {
|
||||||
|
@include button-variant($value, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@each $color, $value in $theme-colors {
|
||||||
|
.btn-outline-#{$color} {
|
||||||
|
@include button-outline-variant($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scss-docs-end btn-variant-loops
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Link buttons
|
||||||
|
//
|
||||||
|
|
||||||
|
// Make a button look and behave like a link
|
||||||
|
.btn-link {
|
||||||
|
--#{$prefix}btn-font-weight: #{$font-weight-normal};
|
||||||
|
--#{$prefix}btn-color: #{$btn-link-color};
|
||||||
|
--#{$prefix}btn-bg: transparent;
|
||||||
|
--#{$prefix}btn-border-color: transparent;
|
||||||
|
--#{$prefix}btn-hover-color: #{$btn-link-hover-color};
|
||||||
|
--#{$prefix}btn-hover-border-color: transparent;
|
||||||
|
--#{$prefix}btn-active-color: #{$btn-link-hover-color};
|
||||||
|
--#{$prefix}btn-active-border-color: transparent;
|
||||||
|
--#{$prefix}btn-disabled-color: #{$btn-link-disabled-color};
|
||||||
|
--#{$prefix}btn-disabled-border-color: transparent;
|
||||||
|
--#{$prefix}btn-box-shadow: 0 0 0 #000; // Can't use `none` as keyword negates all values when used with multiple shadows
|
||||||
|
--#{$prefix}btn-focus-shadow-rgb: #{$btn-link-focus-shadow-rgb};
|
||||||
|
|
||||||
|
text-decoration: $link-decoration;
|
||||||
|
@if $enable-gradients {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible {
|
||||||
|
text-decoration: $link-hover-decoration;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
color: var(--#{$prefix}btn-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--#{$prefix}btn-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need for an active state here
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Button Sizes
|
||||||
|
//
|
||||||
|
|
||||||
|
.btn-lg {
|
||||||
|
@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm);
|
||||||
|
}
|
||||||
238
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_card.scss
vendored
Normal file
238
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_card.scss
vendored
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
//
|
||||||
|
// Base styles
|
||||||
|
//
|
||||||
|
|
||||||
|
.card {
|
||||||
|
// scss-docs-start card-css-vars
|
||||||
|
--#{$prefix}card-spacer-y: #{$card-spacer-y};
|
||||||
|
--#{$prefix}card-spacer-x: #{$card-spacer-x};
|
||||||
|
--#{$prefix}card-title-spacer-y: #{$card-title-spacer-y};
|
||||||
|
--#{$prefix}card-title-color: #{$card-title-color};
|
||||||
|
--#{$prefix}card-subtitle-color: #{$card-subtitle-color};
|
||||||
|
--#{$prefix}card-border-width: #{$card-border-width};
|
||||||
|
--#{$prefix}card-border-color: #{$card-border-color};
|
||||||
|
--#{$prefix}card-border-radius: #{$card-border-radius};
|
||||||
|
--#{$prefix}card-box-shadow: #{$card-box-shadow};
|
||||||
|
--#{$prefix}card-inner-border-radius: #{$card-inner-border-radius};
|
||||||
|
--#{$prefix}card-cap-padding-y: #{$card-cap-padding-y};
|
||||||
|
--#{$prefix}card-cap-padding-x: #{$card-cap-padding-x};
|
||||||
|
--#{$prefix}card-cap-bg: #{$card-cap-bg};
|
||||||
|
--#{$prefix}card-cap-color: #{$card-cap-color};
|
||||||
|
--#{$prefix}card-height: #{$card-height};
|
||||||
|
--#{$prefix}card-color: #{$card-color};
|
||||||
|
--#{$prefix}card-bg: #{$card-bg};
|
||||||
|
--#{$prefix}card-img-overlay-padding: #{$card-img-overlay-padding};
|
||||||
|
--#{$prefix}card-group-margin: #{$card-group-margin};
|
||||||
|
// scss-docs-end card-css-vars
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0; // See https://github.com/twbs/bootstrap/pull/22740#issuecomment-305868106
|
||||||
|
height: var(--#{$prefix}card-height);
|
||||||
|
color: var(--#{$prefix}body-color);
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: var(--#{$prefix}card-bg);
|
||||||
|
background-clip: border-box;
|
||||||
|
border: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
|
||||||
|
@include border-radius(var(--#{$prefix}card-border-radius));
|
||||||
|
@include box-shadow(var(--#{$prefix}card-box-shadow));
|
||||||
|
|
||||||
|
> hr {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .list-group {
|
||||||
|
border-top: inherit;
|
||||||
|
border-bottom: inherit;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
border-top-width: 0;
|
||||||
|
@include border-top-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to specificity of the above selector (`.card > .list-group`), we must
|
||||||
|
// use a child selector here to prevent double borders.
|
||||||
|
> .card-header + .list-group,
|
||||||
|
> .list-group + .card-footer {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
// Enable `flex-grow: 1` for decks and groups so that card blocks take up
|
||||||
|
// as much space as possible, ensuring footers are aligned to the bottom.
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: var(--#{$prefix}card-spacer-y) var(--#{$prefix}card-spacer-x);
|
||||||
|
color: var(--#{$prefix}card-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
margin-bottom: var(--#{$prefix}card-title-spacer-y);
|
||||||
|
color: var(--#{$prefix}card-title-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-subtitle {
|
||||||
|
margin-top: calc(-.5 * var(--#{$prefix}card-title-spacer-y)); // stylelint-disable-line function-disallowed-list
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: var(--#{$prefix}card-subtitle-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-text:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-link {
|
||||||
|
&:hover {
|
||||||
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ .card-link {
|
||||||
|
margin-left: var(--#{$prefix}card-spacer-x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Optional textual caps
|
||||||
|
//
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x);
|
||||||
|
margin-bottom: 0; // Removes the default margin-bottom of <hN>
|
||||||
|
color: var(--#{$prefix}card-cap-color);
|
||||||
|
background-color: var(--#{$prefix}card-cap-bg);
|
||||||
|
border-bottom: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
@include border-radius(var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius) 0 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
padding: var(--#{$prefix}card-cap-padding-y) var(--#{$prefix}card-cap-padding-x);
|
||||||
|
color: var(--#{$prefix}card-cap-color);
|
||||||
|
background-color: var(--#{$prefix}card-cap-bg);
|
||||||
|
border-top: var(--#{$prefix}card-border-width) solid var(--#{$prefix}card-border-color);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
@include border-radius(0 0 var(--#{$prefix}card-inner-border-radius) var(--#{$prefix}card-inner-border-radius));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Header navs
|
||||||
|
//
|
||||||
|
|
||||||
|
.card-header-tabs {
|
||||||
|
margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
|
margin-bottom: calc(-1 * var(--#{$prefix}card-cap-padding-y)); // stylelint-disable-line function-disallowed-list
|
||||||
|
margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
|
border-bottom: 0;
|
||||||
|
|
||||||
|
.nav-link.active {
|
||||||
|
background-color: var(--#{$prefix}card-bg);
|
||||||
|
border-bottom-color: var(--#{$prefix}card-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header-pills {
|
||||||
|
margin-right: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
|
margin-left: calc(-.5 * var(--#{$prefix}card-cap-padding-x)); // stylelint-disable-line function-disallowed-list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card image
|
||||||
|
.card-img-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
padding: var(--#{$prefix}card-img-overlay-padding);
|
||||||
|
@include border-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-img,
|
||||||
|
.card-img-top,
|
||||||
|
.card-img-bottom {
|
||||||
|
width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-img,
|
||||||
|
.card-img-top {
|
||||||
|
@include border-top-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-img,
|
||||||
|
.card-img-bottom {
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}card-inner-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Card groups
|
||||||
|
//
|
||||||
|
|
||||||
|
.card-group {
|
||||||
|
// The child selector allows nested `.card` within `.card-group`
|
||||||
|
// to display properly.
|
||||||
|
> .card {
|
||||||
|
margin-bottom: var(--#{$prefix}card-group-margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
// The child selector allows nested `.card` within `.card-group`
|
||||||
|
// to display properly.
|
||||||
|
> .card {
|
||||||
|
flex: 1 0 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
+ .card {
|
||||||
|
margin-left: 0;
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle rounded corners
|
||||||
|
@if $enable-rounded {
|
||||||
|
&:not(:last-child) {
|
||||||
|
@include border-end-radius(0);
|
||||||
|
|
||||||
|
> .card-img-top,
|
||||||
|
> .card-header {
|
||||||
|
// stylelint-disable-next-line property-disallowed-list
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
> .card-img-bottom,
|
||||||
|
> .card-footer {
|
||||||
|
// stylelint-disable-next-line property-disallowed-list
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
@include border-start-radius(0);
|
||||||
|
|
||||||
|
> .card-img-top,
|
||||||
|
> .card-header {
|
||||||
|
// stylelint-disable-next-line property-disallowed-list
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
}
|
||||||
|
> .card-img-bottom,
|
||||||
|
> .card-footer {
|
||||||
|
// stylelint-disable-next-line property-disallowed-list
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
226
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_carousel.scss
vendored
Normal file
226
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_carousel.scss
vendored
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
// Notes on the classes:
|
||||||
|
//
|
||||||
|
// 1. .carousel.pointer-event should ideally be pan-y (to allow for users to scroll vertically)
|
||||||
|
// even when their scroll action started on a carousel, but for compatibility (with Firefox)
|
||||||
|
// we're preventing all actions instead
|
||||||
|
// 2. The .carousel-item-start and .carousel-item-end is used to indicate where
|
||||||
|
// the active slide is heading.
|
||||||
|
// 3. .active.carousel-item is the current slide.
|
||||||
|
// 4. .active.carousel-item-start and .active.carousel-item-end is the current
|
||||||
|
// slide in its in-transition state. Only one of these occurs at a time.
|
||||||
|
// 5. .carousel-item-next.carousel-item-start and .carousel-item-prev.carousel-item-end
|
||||||
|
// is the upcoming slide in transition.
|
||||||
|
|
||||||
|
.carousel {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel.pointer-event {
|
||||||
|
touch-action: pan-y;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-inner {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
@include clearfix();
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-item {
|
||||||
|
position: relative;
|
||||||
|
display: none;
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
margin-right: -100%;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
@include transition($carousel-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-item.active,
|
||||||
|
.carousel-item-next,
|
||||||
|
.carousel-item-prev {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-item-next:not(.carousel-item-start),
|
||||||
|
.active.carousel-item-end {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-item-prev:not(.carousel-item-end),
|
||||||
|
.active.carousel-item-start {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Alternate transitions
|
||||||
|
//
|
||||||
|
|
||||||
|
.carousel-fade {
|
||||||
|
.carousel-item {
|
||||||
|
opacity: 0;
|
||||||
|
transition-property: opacity;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-item.active,
|
||||||
|
.carousel-item-next.carousel-item-start,
|
||||||
|
.carousel-item-prev.carousel-item-end {
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.carousel-item-start,
|
||||||
|
.active.carousel-item-end {
|
||||||
|
z-index: 0;
|
||||||
|
opacity: 0;
|
||||||
|
@include transition(opacity 0s $carousel-transition-duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Left/right controls for nav
|
||||||
|
//
|
||||||
|
|
||||||
|
.carousel-control-prev,
|
||||||
|
.carousel-control-next {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1;
|
||||||
|
// Use flex for alignment (1-3)
|
||||||
|
display: flex; // 1. allow flex styles
|
||||||
|
align-items: center; // 2. vertically center contents
|
||||||
|
justify-content: center; // 3. horizontally center contents
|
||||||
|
width: $carousel-control-width;
|
||||||
|
padding: 0;
|
||||||
|
color: $carousel-control-color;
|
||||||
|
text-align: center;
|
||||||
|
background: none;
|
||||||
|
filter: var(--#{$prefix}carousel-control-icon-filter);
|
||||||
|
border: 0;
|
||||||
|
opacity: $carousel-control-opacity;
|
||||||
|
@include transition($carousel-control-transition);
|
||||||
|
|
||||||
|
// Hover/focus state
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $carousel-control-color;
|
||||||
|
text-decoration: none;
|
||||||
|
outline: 0;
|
||||||
|
opacity: $carousel-control-hover-opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.carousel-control-prev {
|
||||||
|
left: 0;
|
||||||
|
background-image: if($enable-gradients, linear-gradient(90deg, rgba($black, .25), rgba($black, .001)), null);
|
||||||
|
}
|
||||||
|
.carousel-control-next {
|
||||||
|
right: 0;
|
||||||
|
background-image: if($enable-gradients, linear-gradient(270deg, rgba($black, .25), rgba($black, .001)), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Icons for within
|
||||||
|
.carousel-control-prev-icon,
|
||||||
|
.carousel-control-next-icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: $carousel-control-icon-width;
|
||||||
|
height: $carousel-control-icon-width;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 50%;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-control-prev-icon {
|
||||||
|
background-image: escape-svg($carousel-control-prev-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-next-icon-bg) + "*/"};
|
||||||
|
}
|
||||||
|
.carousel-control-next-icon {
|
||||||
|
background-image: escape-svg($carousel-control-next-icon-bg) #{"/*rtl:" + escape-svg($carousel-control-prev-icon-bg) + "*/"};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional indicator pips/controls
|
||||||
|
//
|
||||||
|
// Add a container (such as a list) with the following class and add an item (ideally a focusable control,
|
||||||
|
// like a button) with data-bs-target for each slide your carousel holds.
|
||||||
|
|
||||||
|
.carousel-indicators {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
// Use the .carousel-control's width as margin so we don't overlay those
|
||||||
|
margin-right: $carousel-control-width;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
margin-left: $carousel-control-width;
|
||||||
|
|
||||||
|
[data-bs-target] {
|
||||||
|
box-sizing: content-box;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
width: $carousel-indicator-width;
|
||||||
|
height: $carousel-indicator-height;
|
||||||
|
padding: 0;
|
||||||
|
margin-right: $carousel-indicator-spacer;
|
||||||
|
margin-left: $carousel-indicator-spacer;
|
||||||
|
text-indent: -999px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--#{$prefix}carousel-indicator-active-bg);
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 0;
|
||||||
|
// Use transparent borders to increase the hit area by 10px on top and bottom.
|
||||||
|
border-top: $carousel-indicator-hit-area-height solid transparent;
|
||||||
|
border-bottom: $carousel-indicator-hit-area-height solid transparent;
|
||||||
|
opacity: $carousel-indicator-opacity;
|
||||||
|
@include transition($carousel-indicator-transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
opacity: $carousel-indicator-active-opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Optional captions
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
.carousel-caption {
|
||||||
|
position: absolute;
|
||||||
|
right: (100% - $carousel-caption-width) * .5;
|
||||||
|
bottom: $carousel-caption-spacer;
|
||||||
|
left: (100% - $carousel-caption-width) * .5;
|
||||||
|
padding-top: $carousel-caption-padding-y;
|
||||||
|
padding-bottom: $carousel-caption-padding-y;
|
||||||
|
color: var(--#{$prefix}carousel-caption-color);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode carousel
|
||||||
|
|
||||||
|
@mixin carousel-dark() {
|
||||||
|
--#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg-dark};
|
||||||
|
--#{$prefix}carousel-caption-color: #{$carousel-caption-color-dark};
|
||||||
|
--#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter-dark};
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-dark {
|
||||||
|
@include carousel-dark();
|
||||||
|
}
|
||||||
|
|
||||||
|
:root,
|
||||||
|
[data-bs-theme="light"] {
|
||||||
|
--#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg};
|
||||||
|
--#{$prefix}carousel-caption-color: #{$carousel-caption-color};
|
||||||
|
--#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter};
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
@include color-mode(dark, true) {
|
||||||
|
@include carousel-dark();
|
||||||
|
}
|
||||||
|
}
|
||||||
66
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_close.scss
vendored
Normal file
66
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_close.scss
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Transparent background and border properties included for button version.
|
||||||
|
// iOS requires the button element instead of an anchor tag.
|
||||||
|
// If you want the anchor version, it requires `href="#"`.
|
||||||
|
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
|
||||||
|
|
||||||
|
.btn-close {
|
||||||
|
// scss-docs-start close-css-vars
|
||||||
|
--#{$prefix}btn-close-color: #{$btn-close-color};
|
||||||
|
--#{$prefix}btn-close-bg: #{ escape-svg($btn-close-bg) };
|
||||||
|
--#{$prefix}btn-close-opacity: #{$btn-close-opacity};
|
||||||
|
--#{$prefix}btn-close-hover-opacity: #{$btn-close-hover-opacity};
|
||||||
|
--#{$prefix}btn-close-focus-shadow: #{$btn-close-focus-shadow};
|
||||||
|
--#{$prefix}btn-close-focus-opacity: #{$btn-close-focus-opacity};
|
||||||
|
--#{$prefix}btn-close-disabled-opacity: #{$btn-close-disabled-opacity};
|
||||||
|
// scss-docs-end close-css-vars
|
||||||
|
|
||||||
|
box-sizing: content-box;
|
||||||
|
width: $btn-close-width;
|
||||||
|
height: $btn-close-height;
|
||||||
|
padding: $btn-close-padding-y $btn-close-padding-x;
|
||||||
|
color: var(--#{$prefix}btn-close-color);
|
||||||
|
background: transparent var(--#{$prefix}btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements
|
||||||
|
filter: var(--#{$prefix}btn-close-filter);
|
||||||
|
border: 0; // for button elements
|
||||||
|
@include border-radius();
|
||||||
|
opacity: var(--#{$prefix}btn-close-opacity);
|
||||||
|
|
||||||
|
// Override <a>'s hover style
|
||||||
|
&:hover {
|
||||||
|
color: var(--#{$prefix}btn-close-color);
|
||||||
|
text-decoration: none;
|
||||||
|
opacity: var(--#{$prefix}btn-close-hover-opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: var(--#{$prefix}btn-close-focus-shadow);
|
||||||
|
opacity: var(--#{$prefix}btn-close-focus-opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
opacity: var(--#{$prefix}btn-close-disabled-opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin btn-close-white() {
|
||||||
|
--#{$prefix}btn-close-filter: #{$btn-close-filter-dark};
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close-white {
|
||||||
|
@include btn-close-white();
|
||||||
|
}
|
||||||
|
|
||||||
|
:root,
|
||||||
|
[data-bs-theme="light"] {
|
||||||
|
--#{$prefix}btn-close-filter: #{$btn-close-filter};
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
@include color-mode(dark, true) {
|
||||||
|
@include btn-close-white();
|
||||||
|
}
|
||||||
|
}
|
||||||
41
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_containers.scss
vendored
Normal file
41
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_containers.scss
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Container widths
|
||||||
|
//
|
||||||
|
// Set the container width, and override it for fixed navbars in media queries.
|
||||||
|
|
||||||
|
@if $enable-container-classes {
|
||||||
|
// Single container class with breakpoint max-widths
|
||||||
|
.container,
|
||||||
|
// 100% wide container at all breakpoints
|
||||||
|
.container-fluid {
|
||||||
|
@include make-container();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responsive containers that are 100% wide until a breakpoint
|
||||||
|
@each $breakpoint, $container-max-width in $container-max-widths {
|
||||||
|
.container-#{$breakpoint} {
|
||||||
|
@extend .container-fluid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up($breakpoint, $grid-breakpoints) {
|
||||||
|
%responsive-container-#{$breakpoint} {
|
||||||
|
max-width: $container-max-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend each breakpoint which is smaller or equal to the current breakpoint
|
||||||
|
$extend-breakpoint: true;
|
||||||
|
|
||||||
|
@each $name, $width in $grid-breakpoints {
|
||||||
|
@if ($extend-breakpoint) {
|
||||||
|
.container#{breakpoint-infix($name, $grid-breakpoints)} {
|
||||||
|
@extend %responsive-container-#{$breakpoint};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the current breakpoint is reached, stop extending
|
||||||
|
@if ($breakpoint == $name) {
|
||||||
|
$extend-breakpoint: false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
250
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_dropdown.scss
vendored
Normal file
250
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_dropdown.scss
vendored
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
// The dropdown wrapper (`<div>`)
|
||||||
|
.dropup,
|
||||||
|
.dropend,
|
||||||
|
.dropdown,
|
||||||
|
.dropstart,
|
||||||
|
.dropup-center,
|
||||||
|
.dropdown-center {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle {
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
// Generate the caret automatically
|
||||||
|
@include caret();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dropdown menu
|
||||||
|
.dropdown-menu {
|
||||||
|
// scss-docs-start dropdown-css-vars
|
||||||
|
--#{$prefix}dropdown-zindex: #{$zindex-dropdown};
|
||||||
|
--#{$prefix}dropdown-min-width: #{$dropdown-min-width};
|
||||||
|
--#{$prefix}dropdown-padding-x: #{$dropdown-padding-x};
|
||||||
|
--#{$prefix}dropdown-padding-y: #{$dropdown-padding-y};
|
||||||
|
--#{$prefix}dropdown-spacer: #{$dropdown-spacer};
|
||||||
|
@include rfs($dropdown-font-size, --#{$prefix}dropdown-font-size);
|
||||||
|
--#{$prefix}dropdown-color: #{$dropdown-color};
|
||||||
|
--#{$prefix}dropdown-bg: #{$dropdown-bg};
|
||||||
|
--#{$prefix}dropdown-border-color: #{$dropdown-border-color};
|
||||||
|
--#{$prefix}dropdown-border-radius: #{$dropdown-border-radius};
|
||||||
|
--#{$prefix}dropdown-border-width: #{$dropdown-border-width};
|
||||||
|
--#{$prefix}dropdown-inner-border-radius: #{$dropdown-inner-border-radius};
|
||||||
|
--#{$prefix}dropdown-divider-bg: #{$dropdown-divider-bg};
|
||||||
|
--#{$prefix}dropdown-divider-margin-y: #{$dropdown-divider-margin-y};
|
||||||
|
--#{$prefix}dropdown-box-shadow: #{$dropdown-box-shadow};
|
||||||
|
--#{$prefix}dropdown-link-color: #{$dropdown-link-color};
|
||||||
|
--#{$prefix}dropdown-link-hover-color: #{$dropdown-link-hover-color};
|
||||||
|
--#{$prefix}dropdown-link-hover-bg: #{$dropdown-link-hover-bg};
|
||||||
|
--#{$prefix}dropdown-link-active-color: #{$dropdown-link-active-color};
|
||||||
|
--#{$prefix}dropdown-link-active-bg: #{$dropdown-link-active-bg};
|
||||||
|
--#{$prefix}dropdown-link-disabled-color: #{$dropdown-link-disabled-color};
|
||||||
|
--#{$prefix}dropdown-item-padding-x: #{$dropdown-item-padding-x};
|
||||||
|
--#{$prefix}dropdown-item-padding-y: #{$dropdown-item-padding-y};
|
||||||
|
--#{$prefix}dropdown-header-color: #{$dropdown-header-color};
|
||||||
|
--#{$prefix}dropdown-header-padding-x: #{$dropdown-header-padding-x};
|
||||||
|
--#{$prefix}dropdown-header-padding-y: #{$dropdown-header-padding-y};
|
||||||
|
// scss-docs-end dropdown-css-vars
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
z-index: var(--#{$prefix}dropdown-zindex);
|
||||||
|
display: none; // none by default, but block on "open" of the menu
|
||||||
|
min-width: var(--#{$prefix}dropdown-min-width);
|
||||||
|
padding: var(--#{$prefix}dropdown-padding-y) var(--#{$prefix}dropdown-padding-x);
|
||||||
|
margin: 0; // Override default margin of ul
|
||||||
|
@include font-size(var(--#{$prefix}dropdown-font-size));
|
||||||
|
color: var(--#{$prefix}dropdown-color);
|
||||||
|
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
|
||||||
|
list-style: none;
|
||||||
|
background-color: var(--#{$prefix}dropdown-bg);
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: var(--#{$prefix}dropdown-border-width) solid var(--#{$prefix}dropdown-border-color);
|
||||||
|
@include border-radius(var(--#{$prefix}dropdown-border-radius));
|
||||||
|
@include box-shadow(var(--#{$prefix}dropdown-box-shadow));
|
||||||
|
|
||||||
|
&[data-bs-popper] {
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
margin-top: var(--#{$prefix}dropdown-spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $dropdown-padding-y == 0 {
|
||||||
|
> .dropdown-item:first-child,
|
||||||
|
> li:first-child .dropdown-item {
|
||||||
|
@include border-top-radius(var(--#{$prefix}dropdown-inner-border-radius));
|
||||||
|
}
|
||||||
|
> .dropdown-item:last-child,
|
||||||
|
> li:last-child .dropdown-item {
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}dropdown-inner-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scss-docs-start responsive-breakpoints
|
||||||
|
// We deliberately hardcode the `bs-` prefix because we check
|
||||||
|
// this custom property in JS to determine Popper's positioning
|
||||||
|
|
||||||
|
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||||
|
@include media-breakpoint-up($breakpoint) {
|
||||||
|
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
|
||||||
|
|
||||||
|
.dropdown-menu#{$infix}-start {
|
||||||
|
--bs-position: start;
|
||||||
|
|
||||||
|
&[data-bs-popper] {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu#{$infix}-end {
|
||||||
|
--bs-position: end;
|
||||||
|
|
||||||
|
&[data-bs-popper] {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scss-docs-end responsive-breakpoints
|
||||||
|
|
||||||
|
// Allow for dropdowns to go bottom up (aka, dropup-menu)
|
||||||
|
// Just add .dropup after the standard .dropdown class and you're set.
|
||||||
|
.dropup {
|
||||||
|
.dropdown-menu[data-bs-popper] {
|
||||||
|
top: auto;
|
||||||
|
bottom: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: var(--#{$prefix}dropdown-spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle {
|
||||||
|
@include caret(up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropend {
|
||||||
|
.dropdown-menu[data-bs-popper] {
|
||||||
|
top: 0;
|
||||||
|
right: auto;
|
||||||
|
left: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: var(--#{$prefix}dropdown-spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle {
|
||||||
|
@include caret(end);
|
||||||
|
&::after {
|
||||||
|
vertical-align: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropstart {
|
||||||
|
.dropdown-menu[data-bs-popper] {
|
||||||
|
top: 0;
|
||||||
|
right: 100%;
|
||||||
|
left: auto;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-right: var(--#{$prefix}dropdown-spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle {
|
||||||
|
@include caret(start);
|
||||||
|
&::before {
|
||||||
|
vertical-align: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Dividers (basically an `<hr>`) within the dropdown
|
||||||
|
.dropdown-divider {
|
||||||
|
height: 0;
|
||||||
|
margin: var(--#{$prefix}dropdown-divider-margin-y) 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-top: 1px solid var(--#{$prefix}dropdown-divider-bg);
|
||||||
|
opacity: 1; // Revisit in v6 to de-dupe styles that conflict with <hr> element
|
||||||
|
}
|
||||||
|
|
||||||
|
// Links, buttons, and more within the dropdown menu
|
||||||
|
//
|
||||||
|
// `<button>`-specific styles are denoted with `// For <button>s`
|
||||||
|
.dropdown-item {
|
||||||
|
display: block;
|
||||||
|
width: 100%; // For `<button>`s
|
||||||
|
padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
|
||||||
|
clear: both;
|
||||||
|
font-weight: $font-weight-normal;
|
||||||
|
color: var(--#{$prefix}dropdown-link-color);
|
||||||
|
text-align: inherit; // For `<button>`s
|
||||||
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
|
white-space: nowrap; // prevent links from randomly breaking onto new lines
|
||||||
|
background-color: transparent; // For `<button>`s
|
||||||
|
border: 0; // For `<button>`s
|
||||||
|
@include border-radius(var(--#{$prefix}dropdown-item-border-radius, 0));
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: var(--#{$prefix}dropdown-link-hover-color);
|
||||||
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
|
@include gradient-bg(var(--#{$prefix}dropdown-link-hover-bg));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
&:active {
|
||||||
|
color: var(--#{$prefix}dropdown-link-active-color);
|
||||||
|
text-decoration: none;
|
||||||
|
@include gradient-bg(var(--#{$prefix}dropdown-link-active-bg));
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled,
|
||||||
|
&:disabled {
|
||||||
|
color: var(--#{$prefix}dropdown-link-disabled-color);
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: transparent;
|
||||||
|
// Remove CSS gradients if they're enabled
|
||||||
|
background-image: if($enable-gradients, none, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dropdown section headers
|
||||||
|
.dropdown-header {
|
||||||
|
display: block;
|
||||||
|
padding: var(--#{$prefix}dropdown-header-padding-y) var(--#{$prefix}dropdown-header-padding-x);
|
||||||
|
margin-bottom: 0; // for use with heading elements
|
||||||
|
@include font-size($font-size-sm);
|
||||||
|
color: var(--#{$prefix}dropdown-header-color);
|
||||||
|
white-space: nowrap; // as with > li > a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dropdown text
|
||||||
|
.dropdown-item-text {
|
||||||
|
display: block;
|
||||||
|
padding: var(--#{$prefix}dropdown-item-padding-y) var(--#{$prefix}dropdown-item-padding-x);
|
||||||
|
color: var(--#{$prefix}dropdown-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark dropdowns
|
||||||
|
.dropdown-menu-dark {
|
||||||
|
// scss-docs-start dropdown-dark-css-vars
|
||||||
|
--#{$prefix}dropdown-color: #{$dropdown-dark-color};
|
||||||
|
--#{$prefix}dropdown-bg: #{$dropdown-dark-bg};
|
||||||
|
--#{$prefix}dropdown-border-color: #{$dropdown-dark-border-color};
|
||||||
|
--#{$prefix}dropdown-box-shadow: #{$dropdown-dark-box-shadow};
|
||||||
|
--#{$prefix}dropdown-link-color: #{$dropdown-dark-link-color};
|
||||||
|
--#{$prefix}dropdown-link-hover-color: #{$dropdown-dark-link-hover-color};
|
||||||
|
--#{$prefix}dropdown-divider-bg: #{$dropdown-dark-divider-bg};
|
||||||
|
--#{$prefix}dropdown-link-hover-bg: #{$dropdown-dark-link-hover-bg};
|
||||||
|
--#{$prefix}dropdown-link-active-color: #{$dropdown-dark-link-active-color};
|
||||||
|
--#{$prefix}dropdown-link-active-bg: #{$dropdown-dark-link-active-bg};
|
||||||
|
--#{$prefix}dropdown-link-disabled-color: #{$dropdown-dark-link-disabled-color};
|
||||||
|
--#{$prefix}dropdown-header-color: #{$dropdown-dark-header-color};
|
||||||
|
// scss-docs-end dropdown-dark-css-vars
|
||||||
|
}
|
||||||
9
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_forms.scss
vendored
Normal file
9
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_forms.scss
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
@import "forms/labels";
|
||||||
|
@import "forms/form-text";
|
||||||
|
@import "forms/form-control";
|
||||||
|
@import "forms/form-select";
|
||||||
|
@import "forms/form-check";
|
||||||
|
@import "forms/form-range";
|
||||||
|
@import "forms/floating-labels";
|
||||||
|
@import "forms/input-group";
|
||||||
|
@import "forms/validation";
|
||||||
302
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_functions.scss
vendored
Normal file
302
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_functions.scss
vendored
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
// Bootstrap functions
|
||||||
|
//
|
||||||
|
// Utility mixins and functions for evaluating source code across our variables, maps, and mixins.
|
||||||
|
|
||||||
|
// Ascending
|
||||||
|
// Used to evaluate Sass maps like our grid breakpoints.
|
||||||
|
@mixin _assert-ascending($map, $map-name) {
|
||||||
|
$prev-key: null;
|
||||||
|
$prev-num: null;
|
||||||
|
@each $key, $num in $map {
|
||||||
|
@if $prev-num == null or unit($num) == "%" or unit($prev-num) == "%" {
|
||||||
|
// Do nothing
|
||||||
|
} @else if not comparable($prev-num, $num) {
|
||||||
|
@warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
|
||||||
|
} @else if $prev-num >= $num {
|
||||||
|
@warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
|
||||||
|
}
|
||||||
|
$prev-key: $key;
|
||||||
|
$prev-num: $num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts at zero
|
||||||
|
// Used to ensure the min-width of the lowest breakpoint starts at 0.
|
||||||
|
@mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") {
|
||||||
|
@if length($map) > 0 {
|
||||||
|
$values: map-values($map);
|
||||||
|
$first-value: nth($values, 1);
|
||||||
|
@if $first-value != 0 {
|
||||||
|
@warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
@function to-rgb($value) {
|
||||||
|
@return red($value), green($value), blue($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stylelint-disable scss/dollar-variable-pattern
|
||||||
|
@function rgba-css-var($identifier, $target) {
|
||||||
|
@if $identifier == "body" and $target == "bg" {
|
||||||
|
@return rgba(var(--#{$prefix}#{$identifier}-bg-rgb), var(--#{$prefix}#{$target}-opacity));
|
||||||
|
} @if $identifier == "body" and $target == "text" {
|
||||||
|
@return rgba(var(--#{$prefix}#{$identifier}-color-rgb), var(--#{$prefix}#{$target}-opacity));
|
||||||
|
} @else {
|
||||||
|
@return rgba(var(--#{$prefix}#{$identifier}-rgb), var(--#{$prefix}#{$target}-opacity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@function map-loop($map, $func, $args...) {
|
||||||
|
$_map: ();
|
||||||
|
|
||||||
|
@each $key, $value in $map {
|
||||||
|
// allow to pass the $key and $value of the map as an function argument
|
||||||
|
$_args: ();
|
||||||
|
@each $arg in $args {
|
||||||
|
$_args: append($_args, if($arg == "$key", $key, if($arg == "$value", $value, $arg)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$_map: map-merge($_map, ($key: call(get-function($func), $_args...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $_map;
|
||||||
|
}
|
||||||
|
// stylelint-enable scss/dollar-variable-pattern
|
||||||
|
|
||||||
|
@function varify($list) {
|
||||||
|
$result: null;
|
||||||
|
@each $entry in $list {
|
||||||
|
$result: append($result, var(--#{$prefix}#{$entry}), space);
|
||||||
|
}
|
||||||
|
@return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal Bootstrap function to turn maps into its negative variant.
|
||||||
|
// It prefixes the keys with `n` and makes the value negative.
|
||||||
|
@function negativify-map($map) {
|
||||||
|
$result: ();
|
||||||
|
@each $key, $value in $map {
|
||||||
|
@if $key != 0 {
|
||||||
|
$result: map-merge($result, ("n" + $key: (-$value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get multiple keys from a sass map
|
||||||
|
@function map-get-multiple($map, $values) {
|
||||||
|
$result: ();
|
||||||
|
@each $key, $value in $map {
|
||||||
|
@if (index($values, $key) != null) {
|
||||||
|
$result: map-merge($result, ($key: $value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge multiple maps
|
||||||
|
@function map-merge-multiple($maps...) {
|
||||||
|
$merged-maps: ();
|
||||||
|
|
||||||
|
@each $map in $maps {
|
||||||
|
$merged-maps: map-merge($merged-maps, $map);
|
||||||
|
}
|
||||||
|
@return $merged-maps;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace `$search` with `$replace` in `$string`
|
||||||
|
// Used on our SVG icon backgrounds for custom forms.
|
||||||
|
//
|
||||||
|
// @author Kitty Giraudel
|
||||||
|
// @param {String} $string - Initial string
|
||||||
|
// @param {String} $search - Substring to replace
|
||||||
|
// @param {String} $replace ('') - New value
|
||||||
|
// @return {String} - Updated string
|
||||||
|
@function str-replace($string, $search, $replace: "") {
|
||||||
|
$index: str-index($string, $search);
|
||||||
|
|
||||||
|
@if $index {
|
||||||
|
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://codepen.io/kevinweber/pen/dXWoRw
|
||||||
|
//
|
||||||
|
// Requires the use of quotes around data URIs.
|
||||||
|
|
||||||
|
@function escape-svg($string) {
|
||||||
|
@if str-index($string, "data:image/svg+xml") {
|
||||||
|
@each $char, $encoded in $escaped-characters {
|
||||||
|
// Do not escape the url brackets
|
||||||
|
@if str-index($string, "url(") == 1 {
|
||||||
|
$string: url("#{str-replace(str-slice($string, 6, -3), $char, $encoded)}");
|
||||||
|
} @else {
|
||||||
|
$string: str-replace($string, $char, $encoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@return $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color contrast
|
||||||
|
// See https://github.com/twbs/bootstrap/pull/30168
|
||||||
|
|
||||||
|
// A list of pre-calculated numbers of pow(divide((divide($value, 255) + .055), 1.055), 2.4). (from 0 to 255)
|
||||||
|
// stylelint-disable-next-line scss/dollar-variable-default, scss/dollar-variable-pattern
|
||||||
|
$_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003 .0033 .0037 .004 .0044 .0048 .0052 .0056 .006 .0065 .007 .0075 .008 .0086 .0091 .0097 .0103 .011 .0116 .0123 .013 .0137 .0144 .0152 .016 .0168 .0176 .0185 .0194 .0203 .0212 .0222 .0232 .0242 .0252 .0262 .0273 .0284 .0296 .0307 .0319 .0331 .0343 .0356 .0369 .0382 .0395 .0409 .0423 .0437 .0452 .0467 .0482 .0497 .0513 .0529 .0545 .0561 .0578 .0595 .0612 .063 .0648 .0666 .0685 .0704 .0723 .0742 .0762 .0782 .0802 .0823 .0844 .0865 .0887 .0908 .0931 .0953 .0976 .0999 .1022 .1046 .107 .1095 .1119 .1144 .117 .1195 .1221 .1248 .1274 .1301 .1329 .1356 .1384 .1413 .1441 .147 .15 .1529 .1559 .159 .162 .1651 .1683 .1714 .1746 .1779 .1812 .1845 .1878 .1912 .1946 .1981 .2016 .2051 .2086 .2122 .2159 .2195 .2232 .227 .2307 .2346 .2384 .2423 .2462 .2502 .2542 .2582 .2623 .2664 .2705 .2747 .2789 .2831 .2874 .2918 .2961 .3005 .305 .3095 .314 .3185 .3231 .3278 .3325 .3372 .3419 .3467 .3515 .3564 .3613 .3663 .3712 .3763 .3813 .3864 .3916 .3968 .402 .4072 .4125 .4179 .4233 .4287 .4342 .4397 .4452 .4508 .4564 .4621 .4678 .4735 .4793 .4851 .491 .4969 .5029 .5089 .5149 .521 .5271 .5333 .5395 .5457 .552 .5583 .5647 .5711 .5776 .5841 .5906 .5972 .6038 .6105 .6172 .624 .6308 .6376 .6445 .6514 .6584 .6654 .6724 .6795 .6867 .6939 .7011 .7084 .7157 .7231 .7305 .7379 .7454 .7529 .7605 .7682 .7758 .7835 .7913 .7991 .807 .8148 .8228 .8308 .8388 .8469 .855 .8632 .8714 .8796 .8879 .8963 .9047 .9131 .9216 .9301 .9387 .9473 .956 .9647 .9734 .9823 .9911 1;
|
||||||
|
|
||||||
|
@function color-contrast($background, $color-contrast-dark: $color-contrast-dark, $color-contrast-light: $color-contrast-light, $min-contrast-ratio: $min-contrast-ratio) {
|
||||||
|
$foregrounds: $color-contrast-light, $color-contrast-dark, $white, $black;
|
||||||
|
$max-ratio: 0;
|
||||||
|
$max-ratio-color: null;
|
||||||
|
|
||||||
|
@each $color in $foregrounds {
|
||||||
|
$contrast-ratio: contrast-ratio($background, $color);
|
||||||
|
@if $contrast-ratio >= $min-contrast-ratio {
|
||||||
|
@return $color;
|
||||||
|
} @else if $contrast-ratio > $max-ratio {
|
||||||
|
$max-ratio: $contrast-ratio;
|
||||||
|
$max-ratio-color: $color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@warn "Found no color leading to #{$min-contrast-ratio}:1 contrast ratio against #{$background}...";
|
||||||
|
|
||||||
|
@return $max-ratio-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@function contrast-ratio($background, $foreground: $color-contrast-light) {
|
||||||
|
$l1: luminance($background);
|
||||||
|
$l2: luminance(opaque($background, $foreground));
|
||||||
|
|
||||||
|
@return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return WCAG2.2 relative luminance
|
||||||
|
// See https://www.w3.org/TR/WCAG/#dfn-relative-luminance
|
||||||
|
// See https://www.w3.org/TR/WCAG/#dfn-contrast-ratio
|
||||||
|
@function luminance($color) {
|
||||||
|
$rgb: (
|
||||||
|
"r": red($color),
|
||||||
|
"g": green($color),
|
||||||
|
"b": blue($color)
|
||||||
|
);
|
||||||
|
|
||||||
|
@each $name, $value in $rgb {
|
||||||
|
$value: if(divide($value, 255) < .04045, divide(divide($value, 255), 12.92), nth($_luminance-list, $value + 1));
|
||||||
|
$rgb: map-merge($rgb, ($name: $value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@return (map-get($rgb, "r") * .2126) + (map-get($rgb, "g") * .7152) + (map-get($rgb, "b") * .0722);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return opaque color
|
||||||
|
// opaque(#fff, rgba(0, 0, 0, .5)) => #808080
|
||||||
|
@function opaque($background, $foreground) {
|
||||||
|
@return mix(rgba($foreground, 1), $background, opacity($foreground) * 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scss-docs-start color-functions
|
||||||
|
// Tint a color: mix a color with white
|
||||||
|
@function tint-color($color, $weight) {
|
||||||
|
@return mix(white, $color, $weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shade a color: mix a color with black
|
||||||
|
@function shade-color($color, $weight) {
|
||||||
|
@return mix(black, $color, $weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shade the color if the weight is positive, else tint it
|
||||||
|
@function shift-color($color, $weight) {
|
||||||
|
@return if($weight > 0, shade-color($color, $weight), tint-color($color, -$weight));
|
||||||
|
}
|
||||||
|
// scss-docs-end color-functions
|
||||||
|
|
||||||
|
// Return valid calc
|
||||||
|
@function add($value1, $value2, $return-calc: true) {
|
||||||
|
@if $value1 == null {
|
||||||
|
@return $value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $value2 == null {
|
||||||
|
@return $value1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {
|
||||||
|
@return $value1 + $value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@return if($return-calc == true, calc(#{$value1} + #{$value2}), $value1 + unquote(" + ") + $value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@function subtract($value1, $value2, $return-calc: true) {
|
||||||
|
@if $value1 == null and $value2 == null {
|
||||||
|
@return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $value1 == null {
|
||||||
|
@return -$value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $value2 == null {
|
||||||
|
@return $value1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($value1) == number and type-of($value2) == number and comparable($value1, $value2) {
|
||||||
|
@return $value1 - $value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if type-of($value2) != number {
|
||||||
|
$value2: unquote("(") + $value2 + unquote(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
@return if($return-calc == true, calc(#{$value1} - #{$value2}), $value1 + unquote(" - ") + $value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@function divide($dividend, $divisor, $precision: 10) {
|
||||||
|
$sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);
|
||||||
|
$dividend: abs($dividend);
|
||||||
|
$divisor: abs($divisor);
|
||||||
|
@if $dividend == 0 {
|
||||||
|
@return 0;
|
||||||
|
}
|
||||||
|
@if $divisor == 0 {
|
||||||
|
@error "Cannot divide by 0";
|
||||||
|
}
|
||||||
|
$remainder: $dividend;
|
||||||
|
$result: 0;
|
||||||
|
$factor: 10;
|
||||||
|
@while ($remainder > 0 and $precision >= 0) {
|
||||||
|
$quotient: 0;
|
||||||
|
@while ($remainder >= $divisor) {
|
||||||
|
$remainder: $remainder - $divisor;
|
||||||
|
$quotient: $quotient + 1;
|
||||||
|
}
|
||||||
|
$result: $result * 10 + $quotient;
|
||||||
|
$factor: $factor * .1;
|
||||||
|
$remainder: $remainder * 10;
|
||||||
|
$precision: $precision - 1;
|
||||||
|
@if ($precision < 0 and $remainder >= $divisor * 5) {
|
||||||
|
$result: $result + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result: $result * $factor * $sign;
|
||||||
|
$dividend-unit: unit($dividend);
|
||||||
|
$divisor-unit: unit($divisor);
|
||||||
|
$unit-map: (
|
||||||
|
"px": 1px,
|
||||||
|
"rem": 1rem,
|
||||||
|
"em": 1em,
|
||||||
|
"%": 1%
|
||||||
|
);
|
||||||
|
@if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {
|
||||||
|
$result: $result * map-get($unit-map, $dividend-unit);
|
||||||
|
}
|
||||||
|
@return $result;
|
||||||
|
}
|
||||||
39
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_grid.scss
vendored
Normal file
39
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_grid.scss
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Row
|
||||||
|
//
|
||||||
|
// Rows contain your columns.
|
||||||
|
|
||||||
|
:root {
|
||||||
|
@each $name, $value in $grid-breakpoints {
|
||||||
|
--#{$prefix}breakpoint-#{$name}: #{$value};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-grid-classes {
|
||||||
|
.row {
|
||||||
|
@include make-row();
|
||||||
|
|
||||||
|
> * {
|
||||||
|
@include make-col-ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $enable-cssgrid {
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr);
|
||||||
|
grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr);
|
||||||
|
gap: var(--#{$prefix}gap, #{$grid-gutter-width});
|
||||||
|
|
||||||
|
@include make-cssgrid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Columns
|
||||||
|
//
|
||||||
|
// Common styles for small and large grid columns
|
||||||
|
|
||||||
|
@if $enable-grid-classes {
|
||||||
|
@include make-grid-columns();
|
||||||
|
}
|
||||||
12
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_helpers.scss
vendored
Normal file
12
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_helpers.scss
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@import "helpers/clearfix";
|
||||||
|
@import "helpers/color-bg";
|
||||||
|
@import "helpers/colored-links";
|
||||||
|
@import "helpers/focus-ring";
|
||||||
|
@import "helpers/icon-link";
|
||||||
|
@import "helpers/ratio";
|
||||||
|
@import "helpers/position";
|
||||||
|
@import "helpers/stacks";
|
||||||
|
@import "helpers/visually-hidden";
|
||||||
|
@import "helpers/stretched-link";
|
||||||
|
@import "helpers/text-truncation";
|
||||||
|
@import "helpers/vr";
|
||||||
42
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_images.scss
vendored
Normal file
42
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_images.scss
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Responsive images (ensure images don't scale beyond their parents)
|
||||||
|
//
|
||||||
|
// This is purposefully opt-in via an explicit class rather than being the default for all `<img>`s.
|
||||||
|
// We previously tried the "images are responsive by default" approach in Bootstrap v2,
|
||||||
|
// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)
|
||||||
|
// which weren't expecting the images within themselves to be involuntarily resized.
|
||||||
|
// See also https://github.com/twbs/bootstrap/issues/18178
|
||||||
|
.img-fluid {
|
||||||
|
@include img-fluid();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Image thumbnails
|
||||||
|
.img-thumbnail {
|
||||||
|
padding: $thumbnail-padding;
|
||||||
|
background-color: $thumbnail-bg;
|
||||||
|
border: $thumbnail-border-width solid $thumbnail-border-color;
|
||||||
|
@include border-radius($thumbnail-border-radius);
|
||||||
|
@include box-shadow($thumbnail-box-shadow);
|
||||||
|
|
||||||
|
// Keep them at most 100% wide
|
||||||
|
@include img-fluid();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Figures
|
||||||
|
//
|
||||||
|
|
||||||
|
.figure {
|
||||||
|
// Ensures the caption's text aligns with the image.
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.figure-img {
|
||||||
|
margin-bottom: $spacer * .5;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.figure-caption {
|
||||||
|
@include font-size($figure-caption-font-size);
|
||||||
|
color: $figure-caption-color;
|
||||||
|
}
|
||||||
199
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_list-group.scss
vendored
Normal file
199
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_list-group.scss
vendored
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// Base class
|
||||||
|
//
|
||||||
|
// Easily usable on <ul>, <ol>, or <div>.
|
||||||
|
|
||||||
|
.list-group {
|
||||||
|
// scss-docs-start list-group-css-vars
|
||||||
|
--#{$prefix}list-group-color: #{$list-group-color};
|
||||||
|
--#{$prefix}list-group-bg: #{$list-group-bg};
|
||||||
|
--#{$prefix}list-group-border-color: #{$list-group-border-color};
|
||||||
|
--#{$prefix}list-group-border-width: #{$list-group-border-width};
|
||||||
|
--#{$prefix}list-group-border-radius: #{$list-group-border-radius};
|
||||||
|
--#{$prefix}list-group-item-padding-x: #{$list-group-item-padding-x};
|
||||||
|
--#{$prefix}list-group-item-padding-y: #{$list-group-item-padding-y};
|
||||||
|
--#{$prefix}list-group-action-color: #{$list-group-action-color};
|
||||||
|
--#{$prefix}list-group-action-hover-color: #{$list-group-action-hover-color};
|
||||||
|
--#{$prefix}list-group-action-hover-bg: #{$list-group-hover-bg};
|
||||||
|
--#{$prefix}list-group-action-active-color: #{$list-group-action-active-color};
|
||||||
|
--#{$prefix}list-group-action-active-bg: #{$list-group-action-active-bg};
|
||||||
|
--#{$prefix}list-group-disabled-color: #{$list-group-disabled-color};
|
||||||
|
--#{$prefix}list-group-disabled-bg: #{$list-group-disabled-bg};
|
||||||
|
--#{$prefix}list-group-active-color: #{$list-group-active-color};
|
||||||
|
--#{$prefix}list-group-active-bg: #{$list-group-active-bg};
|
||||||
|
--#{$prefix}list-group-active-border-color: #{$list-group-active-border-color};
|
||||||
|
// scss-docs-end list-group-css-vars
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
// No need to set list-style: none; since .list-group-item is block level
|
||||||
|
padding-left: 0; // reset padding because ul and ol
|
||||||
|
margin-bottom: 0;
|
||||||
|
@include border-radius(var(--#{$prefix}list-group-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-numbered {
|
||||||
|
list-style-type: none;
|
||||||
|
counter-reset: section;
|
||||||
|
|
||||||
|
> .list-group-item::before {
|
||||||
|
// Increments only this instance of the section counter
|
||||||
|
content: counters(section, ".") ". ";
|
||||||
|
counter-increment: section;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Individual list items
|
||||||
|
//
|
||||||
|
// Use on `li`s or `div`s within the `.list-group` parent.
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
padding: var(--#{$prefix}list-group-item-padding-y) var(--#{$prefix}list-group-item-padding-x);
|
||||||
|
color: var(--#{$prefix}list-group-color);
|
||||||
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
|
background-color: var(--#{$prefix}list-group-bg);
|
||||||
|
border: var(--#{$prefix}list-group-border-width) solid var(--#{$prefix}list-group-border-color);
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
@include border-top-radius(inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
@include border-bottom-radius(inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled,
|
||||||
|
&:disabled {
|
||||||
|
color: var(--#{$prefix}list-group-disabled-color);
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: var(--#{$prefix}list-group-disabled-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include both here for `<a>`s and `<button>`s
|
||||||
|
&.active {
|
||||||
|
z-index: 2; // Place active items above their siblings for proper border styling
|
||||||
|
color: var(--#{$prefix}list-group-active-color);
|
||||||
|
background-color: var(--#{$prefix}list-group-active-bg);
|
||||||
|
border-color: var(--#{$prefix}list-group-active-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// stylelint-disable-next-line scss/selector-no-redundant-nesting-selector
|
||||||
|
& + .list-group-item {
|
||||||
|
border-top-width: 0;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
margin-top: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
|
border-top-width: var(--#{$prefix}list-group-border-width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive list items
|
||||||
|
//
|
||||||
|
// Use anchor or button elements instead of `li`s or `div`s to create interactive
|
||||||
|
// list items. Includes an extra `.active` modifier class for selected items.
|
||||||
|
|
||||||
|
.list-group-item-action {
|
||||||
|
width: 100%; // For `<button>`s (anchors become 100% by default though)
|
||||||
|
color: var(--#{$prefix}list-group-action-color);
|
||||||
|
text-align: inherit; // For `<button>`s (anchors inherit)
|
||||||
|
|
||||||
|
&:not(.active) {
|
||||||
|
// Hover state
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
z-index: 1; // Place hover/focus items above their siblings for proper border styling
|
||||||
|
color: var(--#{$prefix}list-group-action-hover-color);
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: var(--#{$prefix}list-group-action-hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
color: var(--#{$prefix}list-group-action-active-color);
|
||||||
|
background-color: var(--#{$prefix}list-group-action-active-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal
|
||||||
|
//
|
||||||
|
// Change the layout of list group items from vertical (default) to horizontal.
|
||||||
|
|
||||||
|
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||||
|
@include media-breakpoint-up($breakpoint) {
|
||||||
|
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
|
||||||
|
|
||||||
|
.list-group-horizontal#{$infix} {
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
> .list-group-item {
|
||||||
|
&:first-child:not(:last-child) {
|
||||||
|
@include border-bottom-start-radius(var(--#{$prefix}list-group-border-radius));
|
||||||
|
@include border-top-end-radius(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child:not(:first-child) {
|
||||||
|
@include border-top-end-radius(var(--#{$prefix}list-group-border-radius));
|
||||||
|
@include border-bottom-start-radius(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ .list-group-item {
|
||||||
|
border-top-width: var(--#{$prefix}list-group-border-width);
|
||||||
|
border-left-width: 0;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
margin-left: calc(-1 * var(--#{$prefix}list-group-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
|
border-left-width: var(--#{$prefix}list-group-border-width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Flush list items
|
||||||
|
//
|
||||||
|
// Remove borders and border-radius to keep list group items edge-to-edge. Most
|
||||||
|
// useful within other components (e.g., cards).
|
||||||
|
|
||||||
|
.list-group-flush {
|
||||||
|
@include border-radius(0);
|
||||||
|
|
||||||
|
> .list-group-item {
|
||||||
|
border-width: 0 0 var(--#{$prefix}list-group-border-width);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// scss-docs-start list-group-modifiers
|
||||||
|
// List group contextual variants
|
||||||
|
//
|
||||||
|
// Add modifier classes to change text and background color on individual items.
|
||||||
|
// Organizationally, this must come after the `:hover` states.
|
||||||
|
|
||||||
|
@each $state in map-keys($theme-colors) {
|
||||||
|
.list-group-item-#{$state} {
|
||||||
|
--#{$prefix}list-group-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
|
--#{$prefix}list-group-bg: var(--#{$prefix}#{$state}-bg-subtle);
|
||||||
|
--#{$prefix}list-group-border-color: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}list-group-action-hover-color: var(--#{$prefix}emphasis-color);
|
||||||
|
--#{$prefix}list-group-action-hover-bg: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}list-group-action-active-color: var(--#{$prefix}emphasis-color);
|
||||||
|
--#{$prefix}list-group-action-active-bg: var(--#{$prefix}#{$state}-border-subtle);
|
||||||
|
--#{$prefix}list-group-active-color: var(--#{$prefix}#{$state}-bg-subtle);
|
||||||
|
--#{$prefix}list-group-active-bg: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
|
--#{$prefix}list-group-active-border-color: var(--#{$prefix}#{$state}-text-emphasis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scss-docs-end list-group-modifiers
|
||||||
174
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_maps.scss
vendored
Normal file
174
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_maps.scss
vendored
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
// Re-assigned maps
|
||||||
|
//
|
||||||
|
// Placed here so that others can override the default Sass maps and see automatic updates to utilities and more.
|
||||||
|
|
||||||
|
// scss-docs-start theme-colors-rgb
|
||||||
|
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value") !default;
|
||||||
|
// scss-docs-end theme-colors-rgb
|
||||||
|
|
||||||
|
// scss-docs-start theme-text-map
|
||||||
|
$theme-colors-text: (
|
||||||
|
"primary": $primary-text-emphasis,
|
||||||
|
"secondary": $secondary-text-emphasis,
|
||||||
|
"success": $success-text-emphasis,
|
||||||
|
"info": $info-text-emphasis,
|
||||||
|
"warning": $warning-text-emphasis,
|
||||||
|
"danger": $danger-text-emphasis,
|
||||||
|
"light": $light-text-emphasis,
|
||||||
|
"dark": $dark-text-emphasis,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-text-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-bg-subtle-map
|
||||||
|
$theme-colors-bg-subtle: (
|
||||||
|
"primary": $primary-bg-subtle,
|
||||||
|
"secondary": $secondary-bg-subtle,
|
||||||
|
"success": $success-bg-subtle,
|
||||||
|
"info": $info-bg-subtle,
|
||||||
|
"warning": $warning-bg-subtle,
|
||||||
|
"danger": $danger-bg-subtle,
|
||||||
|
"light": $light-bg-subtle,
|
||||||
|
"dark": $dark-bg-subtle,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-bg-subtle-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-border-subtle-map
|
||||||
|
$theme-colors-border-subtle: (
|
||||||
|
"primary": $primary-border-subtle,
|
||||||
|
"secondary": $secondary-border-subtle,
|
||||||
|
"success": $success-border-subtle,
|
||||||
|
"info": $info-border-subtle,
|
||||||
|
"warning": $warning-border-subtle,
|
||||||
|
"danger": $danger-border-subtle,
|
||||||
|
"light": $light-border-subtle,
|
||||||
|
"dark": $dark-border-subtle,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-border-subtle-map
|
||||||
|
|
||||||
|
$theme-colors-text-dark: null !default;
|
||||||
|
$theme-colors-bg-subtle-dark: null !default;
|
||||||
|
$theme-colors-border-subtle-dark: null !default;
|
||||||
|
|
||||||
|
@if $enable-dark-mode {
|
||||||
|
// scss-docs-start theme-text-dark-map
|
||||||
|
$theme-colors-text-dark: (
|
||||||
|
"primary": $primary-text-emphasis-dark,
|
||||||
|
"secondary": $secondary-text-emphasis-dark,
|
||||||
|
"success": $success-text-emphasis-dark,
|
||||||
|
"info": $info-text-emphasis-dark,
|
||||||
|
"warning": $warning-text-emphasis-dark,
|
||||||
|
"danger": $danger-text-emphasis-dark,
|
||||||
|
"light": $light-text-emphasis-dark,
|
||||||
|
"dark": $dark-text-emphasis-dark,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-text-dark-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-bg-subtle-dark-map
|
||||||
|
$theme-colors-bg-subtle-dark: (
|
||||||
|
"primary": $primary-bg-subtle-dark,
|
||||||
|
"secondary": $secondary-bg-subtle-dark,
|
||||||
|
"success": $success-bg-subtle-dark,
|
||||||
|
"info": $info-bg-subtle-dark,
|
||||||
|
"warning": $warning-bg-subtle-dark,
|
||||||
|
"danger": $danger-bg-subtle-dark,
|
||||||
|
"light": $light-bg-subtle-dark,
|
||||||
|
"dark": $dark-bg-subtle-dark,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-bg-subtle-dark-map
|
||||||
|
|
||||||
|
// scss-docs-start theme-border-subtle-dark-map
|
||||||
|
$theme-colors-border-subtle-dark: (
|
||||||
|
"primary": $primary-border-subtle-dark,
|
||||||
|
"secondary": $secondary-border-subtle-dark,
|
||||||
|
"success": $success-border-subtle-dark,
|
||||||
|
"info": $info-border-subtle-dark,
|
||||||
|
"warning": $warning-border-subtle-dark,
|
||||||
|
"danger": $danger-border-subtle-dark,
|
||||||
|
"light": $light-border-subtle-dark,
|
||||||
|
"dark": $dark-border-subtle-dark,
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end theme-border-subtle-dark-map
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilities maps
|
||||||
|
//
|
||||||
|
// Extends the default `$theme-colors` maps to help create our utilities.
|
||||||
|
|
||||||
|
// Come v6, we'll de-dupe these variables. Until then, for backward compatibility, we keep them to reassign.
|
||||||
|
// scss-docs-start utilities-colors
|
||||||
|
$utilities-colors: $theme-colors-rgb !default;
|
||||||
|
// scss-docs-end utilities-colors
|
||||||
|
|
||||||
|
// scss-docs-start utilities-text-colors
|
||||||
|
$utilities-text: map-merge(
|
||||||
|
$utilities-colors,
|
||||||
|
(
|
||||||
|
"black": to-rgb($black),
|
||||||
|
"white": to-rgb($white),
|
||||||
|
"body": to-rgb($body-color)
|
||||||
|
)
|
||||||
|
) !default;
|
||||||
|
$utilities-text-colors: map-loop($utilities-text, rgba-css-var, "$key", "text") !default;
|
||||||
|
|
||||||
|
$utilities-text-emphasis-colors: (
|
||||||
|
"primary-emphasis": var(--#{$prefix}primary-text-emphasis),
|
||||||
|
"secondary-emphasis": var(--#{$prefix}secondary-text-emphasis),
|
||||||
|
"success-emphasis": var(--#{$prefix}success-text-emphasis),
|
||||||
|
"info-emphasis": var(--#{$prefix}info-text-emphasis),
|
||||||
|
"warning-emphasis": var(--#{$prefix}warning-text-emphasis),
|
||||||
|
"danger-emphasis": var(--#{$prefix}danger-text-emphasis),
|
||||||
|
"light-emphasis": var(--#{$prefix}light-text-emphasis),
|
||||||
|
"dark-emphasis": var(--#{$prefix}dark-text-emphasis)
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end utilities-text-colors
|
||||||
|
|
||||||
|
// scss-docs-start utilities-bg-colors
|
||||||
|
$utilities-bg: map-merge(
|
||||||
|
$utilities-colors,
|
||||||
|
(
|
||||||
|
"black": to-rgb($black),
|
||||||
|
"white": to-rgb($white),
|
||||||
|
"body": to-rgb($body-bg)
|
||||||
|
)
|
||||||
|
) !default;
|
||||||
|
$utilities-bg-colors: map-loop($utilities-bg, rgba-css-var, "$key", "bg") !default;
|
||||||
|
|
||||||
|
$utilities-bg-subtle: (
|
||||||
|
"primary-subtle": var(--#{$prefix}primary-bg-subtle),
|
||||||
|
"secondary-subtle": var(--#{$prefix}secondary-bg-subtle),
|
||||||
|
"success-subtle": var(--#{$prefix}success-bg-subtle),
|
||||||
|
"info-subtle": var(--#{$prefix}info-bg-subtle),
|
||||||
|
"warning-subtle": var(--#{$prefix}warning-bg-subtle),
|
||||||
|
"danger-subtle": var(--#{$prefix}danger-bg-subtle),
|
||||||
|
"light-subtle": var(--#{$prefix}light-bg-subtle),
|
||||||
|
"dark-subtle": var(--#{$prefix}dark-bg-subtle)
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end utilities-bg-colors
|
||||||
|
|
||||||
|
// scss-docs-start utilities-border-colors
|
||||||
|
$utilities-border: map-merge(
|
||||||
|
$utilities-colors,
|
||||||
|
(
|
||||||
|
"black": to-rgb($black),
|
||||||
|
"white": to-rgb($white)
|
||||||
|
)
|
||||||
|
) !default;
|
||||||
|
$utilities-border-colors: map-loop($utilities-border, rgba-css-var, "$key", "border") !default;
|
||||||
|
|
||||||
|
$utilities-border-subtle: (
|
||||||
|
"primary-subtle": var(--#{$prefix}primary-border-subtle),
|
||||||
|
"secondary-subtle": var(--#{$prefix}secondary-border-subtle),
|
||||||
|
"success-subtle": var(--#{$prefix}success-border-subtle),
|
||||||
|
"info-subtle": var(--#{$prefix}info-border-subtle),
|
||||||
|
"warning-subtle": var(--#{$prefix}warning-border-subtle),
|
||||||
|
"danger-subtle": var(--#{$prefix}danger-border-subtle),
|
||||||
|
"light-subtle": var(--#{$prefix}light-border-subtle),
|
||||||
|
"dark-subtle": var(--#{$prefix}dark-border-subtle)
|
||||||
|
) !default;
|
||||||
|
// scss-docs-end utilities-border-colors
|
||||||
|
|
||||||
|
$utilities-links-underline: map-loop($utilities-colors, rgba-css-var, "$key", "link-underline") !default;
|
||||||
|
|
||||||
|
$negative-spacers: if($enable-negative-margins, negativify-map($spacers), null) !default;
|
||||||
|
|
||||||
|
$gutters: $spacers !default;
|
||||||
42
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_mixins.scss
vendored
Normal file
42
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_mixins.scss
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Toggles
|
||||||
|
//
|
||||||
|
// Used in conjunction with global variables to enable certain theme features.
|
||||||
|
|
||||||
|
// Vendor
|
||||||
|
@import "vendor/rfs";
|
||||||
|
|
||||||
|
// Deprecate
|
||||||
|
@import "mixins/deprecate";
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
@import "mixins/breakpoints";
|
||||||
|
@import "mixins/color-mode";
|
||||||
|
@import "mixins/color-scheme";
|
||||||
|
@import "mixins/image";
|
||||||
|
@import "mixins/resize";
|
||||||
|
@import "mixins/visually-hidden";
|
||||||
|
@import "mixins/reset-text";
|
||||||
|
@import "mixins/text-truncate";
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
@import "mixins/utilities";
|
||||||
|
|
||||||
|
// Components
|
||||||
|
@import "mixins/backdrop";
|
||||||
|
@import "mixins/buttons";
|
||||||
|
@import "mixins/caret";
|
||||||
|
@import "mixins/pagination";
|
||||||
|
@import "mixins/lists";
|
||||||
|
@import "mixins/forms";
|
||||||
|
@import "mixins/table-variants";
|
||||||
|
|
||||||
|
// Skins
|
||||||
|
@import "mixins/border-radius";
|
||||||
|
@import "mixins/box-shadow";
|
||||||
|
@import "mixins/gradients";
|
||||||
|
@import "mixins/transition";
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
@import "mixins/clearfix";
|
||||||
|
@import "mixins/container";
|
||||||
|
@import "mixins/grid";
|
||||||
240
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_modal.scss
vendored
Normal file
240
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_modal.scss
vendored
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
// stylelint-disable function-disallowed-list
|
||||||
|
|
||||||
|
// .modal-open - body class for killing the scroll
|
||||||
|
// .modal - container to scroll within
|
||||||
|
// .modal-dialog - positioning shell for the actual modal
|
||||||
|
// .modal-content - actual modal w/ bg and corners and stuff
|
||||||
|
|
||||||
|
|
||||||
|
// Container that the modal scrolls within
|
||||||
|
.modal {
|
||||||
|
// scss-docs-start modal-css-vars
|
||||||
|
--#{$prefix}modal-zindex: #{$zindex-modal};
|
||||||
|
--#{$prefix}modal-width: #{$modal-md};
|
||||||
|
--#{$prefix}modal-padding: #{$modal-inner-padding};
|
||||||
|
--#{$prefix}modal-margin: #{$modal-dialog-margin};
|
||||||
|
--#{$prefix}modal-color: #{$modal-content-color};
|
||||||
|
--#{$prefix}modal-bg: #{$modal-content-bg};
|
||||||
|
--#{$prefix}modal-border-color: #{$modal-content-border-color};
|
||||||
|
--#{$prefix}modal-border-width: #{$modal-content-border-width};
|
||||||
|
--#{$prefix}modal-border-radius: #{$modal-content-border-radius};
|
||||||
|
--#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-xs};
|
||||||
|
--#{$prefix}modal-inner-border-radius: #{$modal-content-inner-border-radius};
|
||||||
|
--#{$prefix}modal-header-padding-x: #{$modal-header-padding-x};
|
||||||
|
--#{$prefix}modal-header-padding-y: #{$modal-header-padding-y};
|
||||||
|
--#{$prefix}modal-header-padding: #{$modal-header-padding}; // Todo in v6: Split this padding into x and y
|
||||||
|
--#{$prefix}modal-header-border-color: #{$modal-header-border-color};
|
||||||
|
--#{$prefix}modal-header-border-width: #{$modal-header-border-width};
|
||||||
|
--#{$prefix}modal-title-line-height: #{$modal-title-line-height};
|
||||||
|
--#{$prefix}modal-footer-gap: #{$modal-footer-margin-between};
|
||||||
|
--#{$prefix}modal-footer-bg: #{$modal-footer-bg};
|
||||||
|
--#{$prefix}modal-footer-border-color: #{$modal-footer-border-color};
|
||||||
|
--#{$prefix}modal-footer-border-width: #{$modal-footer-border-width};
|
||||||
|
// scss-docs-end modal-css-vars
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: var(--#{$prefix}modal-zindex);
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
// Prevent Chrome on Windows from adding a focus outline. For details, see
|
||||||
|
// https://github.com/twbs/bootstrap/pull/10951.
|
||||||
|
outline: 0;
|
||||||
|
// We deliberately don't use `-webkit-overflow-scrolling: touch;` due to a
|
||||||
|
// gnarly iOS Safari bug: https://bugs.webkit.org/show_bug.cgi?id=158342
|
||||||
|
// See also https://github.com/twbs/bootstrap/issues/17695
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shell div to position the modal with bottom padding
|
||||||
|
.modal-dialog {
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
margin: var(--#{$prefix}modal-margin);
|
||||||
|
// allow clicks to pass through for custom click handling to close modal
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
// When fading in the modal, animate it to slide down
|
||||||
|
.modal.fade & {
|
||||||
|
transform: $modal-fade-transform;
|
||||||
|
@include transition($modal-transition);
|
||||||
|
}
|
||||||
|
.modal.show & {
|
||||||
|
transform: $modal-show-transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When trying to close, animate focus to scale
|
||||||
|
.modal.modal-static & {
|
||||||
|
transform: $modal-scale-transform;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-dialog-scrollable {
|
||||||
|
height: calc(100% - var(--#{$prefix}modal-margin) * 2);
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
max-height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-dialog-centered {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: calc(100% - var(--#{$prefix}modal-margin) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual modal
|
||||||
|
.modal-content {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%; // Ensure `.modal-content` extends the full width of the parent `.modal-dialog`
|
||||||
|
// counteract the pointer-events: none; in the .modal-dialog
|
||||||
|
color: var(--#{$prefix}modal-color);
|
||||||
|
pointer-events: auto;
|
||||||
|
background-color: var(--#{$prefix}modal-bg);
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: var(--#{$prefix}modal-border-width) solid var(--#{$prefix}modal-border-color);
|
||||||
|
@include border-radius(var(--#{$prefix}modal-border-radius));
|
||||||
|
@include box-shadow(var(--#{$prefix}modal-box-shadow));
|
||||||
|
// Remove focus outline from opened modal
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal background
|
||||||
|
.modal-backdrop {
|
||||||
|
// scss-docs-start modal-backdrop-css-vars
|
||||||
|
--#{$prefix}backdrop-zindex: #{$zindex-modal-backdrop};
|
||||||
|
--#{$prefix}backdrop-bg: #{$modal-backdrop-bg};
|
||||||
|
--#{$prefix}backdrop-opacity: #{$modal-backdrop-opacity};
|
||||||
|
// scss-docs-end modal-backdrop-css-vars
|
||||||
|
|
||||||
|
@include overlay-backdrop(var(--#{$prefix}backdrop-zindex), var(--#{$prefix}backdrop-bg), var(--#{$prefix}backdrop-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal header
|
||||||
|
// Top section of the modal w/ title and dismiss
|
||||||
|
.modal-header {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--#{$prefix}modal-header-padding);
|
||||||
|
border-bottom: var(--#{$prefix}modal-header-border-width) solid var(--#{$prefix}modal-header-border-color);
|
||||||
|
@include border-top-radius(var(--#{$prefix}modal-inner-border-radius));
|
||||||
|
|
||||||
|
.btn-close {
|
||||||
|
padding: calc(var(--#{$prefix}modal-header-padding-y) * .5) calc(var(--#{$prefix}modal-header-padding-x) * .5);
|
||||||
|
// Split properties to avoid invalid calc() function if value is 0
|
||||||
|
margin-top: calc(-.5 * var(--#{$prefix}modal-header-padding-y));
|
||||||
|
margin-right: calc(-.5 * var(--#{$prefix}modal-header-padding-x));
|
||||||
|
margin-bottom: calc(-.5 * var(--#{$prefix}modal-header-padding-y));
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title text within header
|
||||||
|
.modal-title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: var(--#{$prefix}modal-title-line-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal body
|
||||||
|
// Where all modal content resides (sibling of .modal-header and .modal-footer)
|
||||||
|
.modal-body {
|
||||||
|
position: relative;
|
||||||
|
// Enable `flex-grow: 1` so that the body take up as much space as possible
|
||||||
|
// when there should be a fixed height on `.modal-dialog`.
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: var(--#{$prefix}modal-padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Footer (for actions)
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center; // vertically center
|
||||||
|
justify-content: flex-end; // Right align buttons with flex property because text-align doesn't work on flex items
|
||||||
|
padding: calc(var(--#{$prefix}modal-padding) - var(--#{$prefix}modal-footer-gap) * .5);
|
||||||
|
background-color: var(--#{$prefix}modal-footer-bg);
|
||||||
|
border-top: var(--#{$prefix}modal-footer-border-width) solid var(--#{$prefix}modal-footer-border-color);
|
||||||
|
@include border-bottom-radius(var(--#{$prefix}modal-inner-border-radius));
|
||||||
|
|
||||||
|
// Place margin between footer elements
|
||||||
|
// This solution is far from ideal because of the universal selector usage,
|
||||||
|
// but is needed to fix https://github.com/twbs/bootstrap/issues/24800
|
||||||
|
> * {
|
||||||
|
margin: calc(var(--#{$prefix}modal-footer-gap) * .5); // Todo in v6: replace with gap on parent class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale up the modal
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
|
.modal {
|
||||||
|
--#{$prefix}modal-margin: #{$modal-dialog-margin-y-sm-up};
|
||||||
|
--#{$prefix}modal-box-shadow: #{$modal-content-box-shadow-sm-up};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically set modal's width for larger viewports
|
||||||
|
.modal-dialog {
|
||||||
|
max-width: var(--#{$prefix}modal-width);
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-sm {
|
||||||
|
--#{$prefix}modal-width: #{$modal-sm};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
.modal-lg,
|
||||||
|
.modal-xl {
|
||||||
|
--#{$prefix}modal-width: #{$modal-lg};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-up(xl) {
|
||||||
|
.modal-xl {
|
||||||
|
--#{$prefix}modal-width: #{$modal-xl};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// scss-docs-start modal-fullscreen-loop
|
||||||
|
@each $breakpoint in map-keys($grid-breakpoints) {
|
||||||
|
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
|
||||||
|
$postfix: if($infix != "", $infix + "-down", "");
|
||||||
|
|
||||||
|
@include media-breakpoint-down($breakpoint) {
|
||||||
|
.modal-fullscreen#{$postfix} {
|
||||||
|
width: 100vw;
|
||||||
|
max-width: none;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
@include border-radius(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header,
|
||||||
|
.modal-footer {
|
||||||
|
@include border-radius(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// scss-docs-end modal-fullscreen-loop
|
||||||
197
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_nav.scss
vendored
Normal file
197
webseite-ts-claude/frontend/dev/scss/01_frameworks/bootstrap-5.3.8/_nav.scss
vendored
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
// Base class
|
||||||
|
//
|
||||||
|
// Kickstart any navigation component with a set of style resets. Works with
|
||||||
|
// `<nav>`s, `<ul>`s or `<ol>`s.
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
// scss-docs-start nav-css-vars
|
||||||
|
--#{$prefix}nav-link-padding-x: #{$nav-link-padding-x};
|
||||||
|
--#{$prefix}nav-link-padding-y: #{$nav-link-padding-y};
|
||||||
|
@include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
|
||||||
|
--#{$prefix}nav-link-font-weight: #{$nav-link-font-weight};
|
||||||
|
--#{$prefix}nav-link-color: #{$nav-link-color};
|
||||||
|
--#{$prefix}nav-link-hover-color: #{$nav-link-hover-color};
|
||||||
|
--#{$prefix}nav-link-disabled-color: #{$nav-link-disabled-color};
|
||||||
|
// scss-docs-end nav-css-vars
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-left: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
display: block;
|
||||||
|
padding: var(--#{$prefix}nav-link-padding-y) var(--#{$prefix}nav-link-padding-x);
|
||||||
|
@include font-size(var(--#{$prefix}nav-link-font-size));
|
||||||
|
font-weight: var(--#{$prefix}nav-link-font-weight);
|
||||||
|
color: var(--#{$prefix}nav-link-color);
|
||||||
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
@include transition($nav-link-transition);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: var(--#{$prefix}nav-link-hover-color);
|
||||||
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: $nav-link-focus-box-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disabled state lightens text
|
||||||
|
&.disabled,
|
||||||
|
&:disabled {
|
||||||
|
color: var(--#{$prefix}nav-link-disabled-color);
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tabs
|
||||||
|
//
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
// scss-docs-start nav-tabs-css-vars
|
||||||
|
--#{$prefix}nav-tabs-border-width: #{$nav-tabs-border-width};
|
||||||
|
--#{$prefix}nav-tabs-border-color: #{$nav-tabs-border-color};
|
||||||
|
--#{$prefix}nav-tabs-border-radius: #{$nav-tabs-border-radius};
|
||||||
|
--#{$prefix}nav-tabs-link-hover-border-color: #{$nav-tabs-link-hover-border-color};
|
||||||
|
--#{$prefix}nav-tabs-link-active-color: #{$nav-tabs-link-active-color};
|
||||||
|
--#{$prefix}nav-tabs-link-active-bg: #{$nav-tabs-link-active-bg};
|
||||||
|
--#{$prefix}nav-tabs-link-active-border-color: #{$nav-tabs-link-active-border-color};
|
||||||
|
// scss-docs-end nav-tabs-css-vars
|
||||||
|
|
||||||
|
border-bottom: var(--#{$prefix}nav-tabs-border-width) solid var(--#{$prefix}nav-tabs-border-color);
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
margin-bottom: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
|
border: var(--#{$prefix}nav-tabs-border-width) solid transparent;
|
||||||
|
@include border-top-radius(var(--#{$prefix}nav-tabs-border-radius));
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
// Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
|
||||||
|
isolation: isolate;
|
||||||
|
border-color: var(--#{$prefix}nav-tabs-link-hover-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active,
|
||||||
|
.nav-item.show .nav-link {
|
||||||
|
color: var(--#{$prefix}nav-tabs-link-active-color);
|
||||||
|
background-color: var(--#{$prefix}nav-tabs-link-active-bg);
|
||||||
|
border-color: var(--#{$prefix}nav-tabs-link-active-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
// Make dropdown border overlap tab border
|
||||||
|
margin-top: calc(-1 * var(--#{$prefix}nav-tabs-border-width)); // stylelint-disable-line function-disallowed-list
|
||||||
|
// Remove the top rounded corners here since there is a hard edge above the menu
|
||||||
|
@include border-top-radius(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pills
|
||||||
|
//
|
||||||
|
|
||||||
|
.nav-pills {
|
||||||
|
// scss-docs-start nav-pills-css-vars
|
||||||
|
--#{$prefix}nav-pills-border-radius: #{$nav-pills-border-radius};
|
||||||
|
--#{$prefix}nav-pills-link-active-color: #{$nav-pills-link-active-color};
|
||||||
|
--#{$prefix}nav-pills-link-active-bg: #{$nav-pills-link-active-bg};
|
||||||
|
// scss-docs-end nav-pills-css-vars
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
@include border-radius(var(--#{$prefix}nav-pills-border-radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active,
|
||||||
|
.show > .nav-link {
|
||||||
|
color: var(--#{$prefix}nav-pills-link-active-color);
|
||||||
|
@include gradient-bg(var(--#{$prefix}nav-pills-link-active-bg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Underline
|
||||||
|
//
|
||||||
|
|
||||||
|
.nav-underline {
|
||||||
|
// scss-docs-start nav-underline-css-vars
|
||||||
|
--#{$prefix}nav-underline-gap: #{$nav-underline-gap};
|
||||||
|
--#{$prefix}nav-underline-border-width: #{$nav-underline-border-width};
|
||||||
|
--#{$prefix}nav-underline-link-active-color: #{$nav-underline-link-active-color};
|
||||||
|
// scss-docs-end nav-underline-css-vars
|
||||||
|
|
||||||
|
gap: var(--#{$prefix}nav-underline-gap);
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
border-bottom: var(--#{$prefix}nav-underline-border-width) solid transparent;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
border-bottom-color: currentcolor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link.active,
|
||||||
|
.show > .nav-link {
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
color: var(--#{$prefix}nav-underline-link-active-color);
|
||||||
|
border-bottom-color: currentcolor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Justified variants
|
||||||
|
//
|
||||||
|
|
||||||
|
.nav-fill {
|
||||||
|
> .nav-link,
|
||||||
|
.nav-item {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-justified {
|
||||||
|
> .nav-link,
|
||||||
|
.nav-item {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-basis: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-fill,
|
||||||
|
.nav-justified {
|
||||||
|
.nav-item .nav-link {
|
||||||
|
width: 100%; // Make sure button will grow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Tabbable tabs
|
||||||
|
//
|
||||||
|
// Hide tabbable panes to start, show them when `.active`
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
> .tab-pane {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
> .active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user