@profitlich/template-toolkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/menu-toggle/MenuToggle.js +124 -0
- package/components/menu-toggle/menu-toggle.scss +14 -0
- package/components/mux-player/MuxPlayer.js +106 -0
- package/components/mux-player/mux-player.scss +3 -0
- package/dev/Dev.js +19 -0
- package/dev/formie-fill-in/FormieFillIn.js +91 -0
- package/dev/toolbar/Toolbar.js +102 -0
- package/dev/toolbar/toolbar.scss +102 -0
- package/package.json +51 -0
- package/scripts/copy-files.js +74 -0
- package/scripts/deploy.js +112 -0
- package/scss/core/hamburger.scss +153 -0
- package/scss/core/layout.scss +238 -0
- package/scss/core/mediaqueries.scss +42 -0
- package/scss/forward.scss +4 -0
- package/utils/BodyScrolled.js +31 -0
- package/utils/MediaQueries.js +56 -0
- package/utils/Vh100.js +31 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import ftp from 'basic-ftp';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import readline from 'readline';
|
|
6
|
+
import cliProgress from 'cli-progress';
|
|
7
|
+
|
|
8
|
+
// Helper for making sure the upload progress bars go to 100%
|
|
9
|
+
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
10
|
+
|
|
11
|
+
export async function runDeploy(mode, uploadTasks) {
|
|
12
|
+
const client = new ftp.Client();
|
|
13
|
+
client.ftp.verbose = false;
|
|
14
|
+
let activeProgressBar = null;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const modeUpper = mode.toUpperCase();
|
|
18
|
+
console.log(`š Starting deployment for: ${modeUpper}`);
|
|
19
|
+
|
|
20
|
+
await client.access({
|
|
21
|
+
host: process.env[`FTP_HOST_${modeUpper}`],
|
|
22
|
+
user: process.env[`FTP_USER_${modeUpper}`],
|
|
23
|
+
password: process.env[`FTP_PASSWORD_${modeUpper}`],
|
|
24
|
+
secure: true
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
for (const task of uploadTasks) {
|
|
28
|
+
console.log(`\nProcessing Task: ${task.name}`);
|
|
29
|
+
|
|
30
|
+
const files = await glob(task.localPattern, {
|
|
31
|
+
nodir: true,
|
|
32
|
+
dot: true,
|
|
33
|
+
ignore: task.ignore
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (files.length === 0) {
|
|
37
|
+
console.log('No files found for this task.');
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const newBar = new cliProgress.SingleBar({
|
|
42
|
+
format: ' Upload |{bar}| {percentage}% {value}/{total} files {duration_formatted}',
|
|
43
|
+
barCompleteChar: 'ā',
|
|
44
|
+
barIncompleteChar: 'ā',
|
|
45
|
+
hideCursor: true
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
activeProgressBar = newBar;
|
|
49
|
+
activeProgressBar.start(files.length, 0);
|
|
50
|
+
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
const relativeFile = path.relative(task.localBase, file);
|
|
53
|
+
const remotePath = path.join(task.remoteDir, relativeFile).replace(/\\/g, '/');
|
|
54
|
+
|
|
55
|
+
await client.ensureDir(path.dirname(remotePath));
|
|
56
|
+
await client.uploadFrom(file, remotePath);
|
|
57
|
+
|
|
58
|
+
activeProgressBar.increment();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
activeProgressBar.stop();
|
|
62
|
+
activeProgressBar = null;
|
|
63
|
+
await delay(50);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log('\nā
Deployment completed successfully!');
|
|
67
|
+
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (activeProgressBar) {
|
|
70
|
+
activeProgressBar.stop();
|
|
71
|
+
}
|
|
72
|
+
console.error('Deployment failed:', err);
|
|
73
|
+
} finally {
|
|
74
|
+
client.close();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Run the deploy script.
|
|
80
|
+
* @param {Array} uploadTasks - array of { name, localPattern, localBase, remoteDir, ignore? }
|
|
81
|
+
*/
|
|
82
|
+
export function run(uploadTasks) {
|
|
83
|
+
const mode = process.argv[2];
|
|
84
|
+
if (!mode || (mode !== 'staging' && mode !== 'production')) {
|
|
85
|
+
console.error('Error: a mode needs to be given, either "staging" or "production"');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
dotenv.config();
|
|
90
|
+
|
|
91
|
+
// In production mode prompt for confirmation
|
|
92
|
+
if (mode === 'production') {
|
|
93
|
+
const rl = readline.createInterface({
|
|
94
|
+
input: process.stdin,
|
|
95
|
+
output: process.stdout
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
rl.question('š“ Do you really want to deploy to PRODUCTION? Type "yes" for confirmation: ', (answer) => {
|
|
99
|
+
rl.close();
|
|
100
|
+
if (answer.toLowerCase() === 'yes') {
|
|
101
|
+
console.log('Confirmed. Starting upload...');
|
|
102
|
+
runDeploy(mode, uploadTasks);
|
|
103
|
+
} else {
|
|
104
|
+
console.log('ā Deployment aborted.');
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// In all other modes run deploy without prompt
|
|
109
|
+
} else {
|
|
110
|
+
runDeploy(mode, uploadTasks);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
@mixin hamburger($hamburger-breite, $hamburger-hoehe, $hamburger-line-width, $hamburger-color, $hamburger-color-active) {
|
|
2
|
+
|
|
3
|
+
// Settings
|
|
4
|
+
// ==================================================
|
|
5
|
+
$hamburger-padding-x : 15px !default;
|
|
6
|
+
$hamburger-padding-y : 15px !default;
|
|
7
|
+
$hamburger-line-gap : calc(($hamburger-hoehe - 3 * $hamburger-line-width) / 2) !default;
|
|
8
|
+
$hamburger-color : #000 !default;
|
|
9
|
+
$hamburger-layer-border-radius : 0 !default;
|
|
10
|
+
$hamburger-hover-opacity : 1 !default;
|
|
11
|
+
$hamburger-active-layer-color : $hamburger-color-active !default;
|
|
12
|
+
$hamburger-active-hover-opacity: $hamburger-hover-opacity !default;
|
|
13
|
+
$hamburger-animation-timing: 0.195s;
|
|
14
|
+
$hamburger-compression-speed: 0.12s;
|
|
15
|
+
$hamburger-turn-speed: $hamburger-animation-timing - $hamburger-compression-speed;
|
|
16
|
+
|
|
17
|
+
// To use CSS filters as the hover effect instead of opacity,
|
|
18
|
+
// set $hamburger-hover-use-filter as true and
|
|
19
|
+
// change the value of $hamburger-hover-filter accordingly.
|
|
20
|
+
$hamburger-hover-use-filter : false !default;
|
|
21
|
+
$hamburger-hover-filter : opacity(50%) !default;
|
|
22
|
+
$hamburger-active-hover-filter: $hamburger-hover-filter !default;
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// padding: $hamburger-padding-y $hamburger-padding-x;
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
@if $hamburger-hover-use-filter==true {
|
|
30
|
+
filter: $hamburger-hover-filter;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@else {
|
|
34
|
+
opacity: $hamburger-hover-opacity;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
body[data-menu-active='true'] & {
|
|
39
|
+
&:hover {
|
|
40
|
+
@if $hamburger-hover-use-filter==true {
|
|
41
|
+
filter: $hamburger-active-hover-filter;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@else {
|
|
45
|
+
opacity: $hamburger-active-hover-opacity;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.hamburger-inner,
|
|
50
|
+
.hamburger-inner::before,
|
|
51
|
+
.hamburger-inner::after {
|
|
52
|
+
background-color: $hamburger-active-layer-color;
|
|
53
|
+
transition: 0s $hamburger-compression-speed linear;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.hamburger-box {
|
|
58
|
+
width: $hamburger-breite;
|
|
59
|
+
height: calc($hamburger-line-width * 3 + $hamburger-line-gap * 2);
|
|
60
|
+
display: inline-block;
|
|
61
|
+
position: relative;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.hamburger-inner {
|
|
65
|
+
display: block;
|
|
66
|
+
top: 50%;
|
|
67
|
+
margin-top: calc($hamburger-line-width / -2);
|
|
68
|
+
|
|
69
|
+
&,
|
|
70
|
+
&::before,
|
|
71
|
+
&::after {
|
|
72
|
+
width: $hamburger-breite;
|
|
73
|
+
height: $hamburger-line-width;
|
|
74
|
+
background-color: $hamburger-color;
|
|
75
|
+
border-radius: $hamburger-layer-border-radius;
|
|
76
|
+
position: absolute;
|
|
77
|
+
transition-property: transform;
|
|
78
|
+
transition-duration: 0.15s;
|
|
79
|
+
transition-timing-function: ease;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
&::before,
|
|
83
|
+
&::after {
|
|
84
|
+
content: "";
|
|
85
|
+
display: block;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
&::before {
|
|
89
|
+
top: calc(($hamburger-line-gap + $hamburger-line-width) * -1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&::after {
|
|
93
|
+
bottom: calc(($hamburger-line-gap + $hamburger-line-width) * -1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
// Aus Basis
|
|
100
|
+
display: block;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
|
|
103
|
+
transition-property: opacity, filter;
|
|
104
|
+
transition-duration: 0.15s;
|
|
105
|
+
transition-timing-function: linear;
|
|
106
|
+
|
|
107
|
+
// Normalize (<button>)
|
|
108
|
+
font: inherit;
|
|
109
|
+
color: inherit;
|
|
110
|
+
text-transform: none;
|
|
111
|
+
background-color: transparent;
|
|
112
|
+
border: 0;
|
|
113
|
+
margin: 0;
|
|
114
|
+
overflow: visible;
|
|
115
|
+
line-height: 0;
|
|
116
|
+
|
|
117
|
+
.hamburger-inner {
|
|
118
|
+
transition-duration: $hamburger-turn-speed;
|
|
119
|
+
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
|
120
|
+
|
|
121
|
+
&::before {
|
|
122
|
+
transition: top $hamburger-turn-speed $hamburger-compression-speed ease,
|
|
123
|
+
opacity $hamburger-turn-speed ease;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
&::after {
|
|
127
|
+
transition: bottom $hamburger-turn-speed $hamburger-compression-speed ease,
|
|
128
|
+
transform $hamburger-turn-speed cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
body[data-menu-active='true'] & {
|
|
133
|
+
.hamburger-inner {
|
|
134
|
+
transform: rotate(45deg);
|
|
135
|
+
transition-delay: $hamburger-compression-speed;
|
|
136
|
+
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
137
|
+
|
|
138
|
+
&::before {
|
|
139
|
+
top: 0;
|
|
140
|
+
opacity: 0;
|
|
141
|
+
transition: top $hamburger-turn-speed ease,
|
|
142
|
+
opacity $hamburger-turn-speed $hamburger-compression-speed ease;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
&::after {
|
|
146
|
+
bottom: 0;
|
|
147
|
+
transform: rotate(-90deg);
|
|
148
|
+
transition: bottom $hamburger-turn-speed ease,
|
|
149
|
+
transform $hamburger-turn-speed $hamburger-compression-speed cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
@use "config";
|
|
2
|
+
@use "sass:list";
|
|
3
|
+
@use "sass:map";
|
|
4
|
+
@use "sass:meta";
|
|
5
|
+
@use "sass:math";
|
|
6
|
+
|
|
7
|
+
// Media queries get the breakpoints
|
|
8
|
+
// Breakpints get the layouts
|
|
9
|
+
// Depending on the project, only a part of the layout names are used,
|
|
10
|
+
// so there needs to be an intervention in the breakpoints to deliver a number even if a layout is not present.
|
|
11
|
+
// Example: There are only the layouts smartphone, tablet and desktop.
|
|
12
|
+
// Then tabletPortrait must deliver the value of tablet
|
|
13
|
+
|
|
14
|
+
$tabletPortrait: null;
|
|
15
|
+
@if map.get(config.$layouts, tabletPortrait) {
|
|
16
|
+
$tabletPortrait: tabletPortrait;
|
|
17
|
+
// no tabletPortrait size is defined
|
|
18
|
+
} @else {
|
|
19
|
+
@if map.get(config.$layouts, tablet) {
|
|
20
|
+
$tabletPortrait: tablet;
|
|
21
|
+
// neither tablet size is defined
|
|
22
|
+
} @else {
|
|
23
|
+
$tabletPortrait: desktop;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
$bigFrom: null;
|
|
28
|
+
@if map.get(config.$layouts, big) {
|
|
29
|
+
$bigFrom: big;
|
|
30
|
+
} @else {
|
|
31
|
+
$bigFrom: 999999px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
$breakpointsVariables: (
|
|
35
|
+
smartphoneTo: map.get(config.$breakpoints, $tabletPortrait) - 1px,
|
|
36
|
+
tabletPortraitFrom: map.get(config.$breakpoints, $tabletPortrait) + 0px,
|
|
37
|
+
tabletPortraitTo: map.get(config.$breakpoints, tabletLandscape) - 1px,
|
|
38
|
+
tabletLandscapeFrom:map.get(config.$breakpoints, tabletLandscape) + 0px,
|
|
39
|
+
tabletLandscapeTo: map.get(config.$breakpoints, desktop) - 1px,
|
|
40
|
+
desktopFrom: map.get(config.$breakpoints, desktop) + 0px,
|
|
41
|
+
desktopTo: map.get(config.$breakpoints, big) - 1px,
|
|
42
|
+
bigFrom: map.get(config.$breakpoints, big) + 0px,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Gibt es kein Layout big, soll die media query desktop keine max-width haben
|
|
46
|
+
$mediaqueryDesktop: null;
|
|
47
|
+
@if map.get(config.$layouts, big) {
|
|
48
|
+
$mediaqueryDesktop: (min-width: map.get($breakpointsVariables, desktopFrom), max-width: map.get($breakpointsVariables, desktopTo));
|
|
49
|
+
} @else {
|
|
50
|
+
$mediaqueryDesktop: (min-width: map.get($breakpointsVariables, desktopFrom));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
$mediaqueries: (
|
|
54
|
+
smartphone: (max-width: map.get($breakpointsVariables, smartphoneTo)),
|
|
55
|
+
tablet: (min-width: map.get($breakpointsVariables, tabletPortraitFrom), max-width: map.get($breakpointsVariables, tabletLandscapeTo)),
|
|
56
|
+
tabletFrom: (min-width: map.get($breakpointsVariables, tabletPortraitFrom)),
|
|
57
|
+
tabletTo: (max-width: map.get($breakpointsVariables, tabletLandscapeTo)),
|
|
58
|
+
tabletPortraitFrom: (min-width: map.get($breakpointsVariables, tabletPortraitFrom)),
|
|
59
|
+
tabletPortrait: (min-width: map.get($breakpointsVariables, tabletPortraitFrom), max-width: map.get($breakpointsVariables, tabletPortraitTo)),
|
|
60
|
+
tabletPortraitTo: (max-width: map.get($breakpointsVariables, tabletPortraitTo)),
|
|
61
|
+
tabletLandscapeFrom:(min-width: map.get($breakpointsVariables, tabletLandscapeFrom)),
|
|
62
|
+
tabletLandscape: (min-width: map.get($breakpointsVariables, tabletLandscapeFrom), max-width: map.get($breakpointsVariables, tabletLandscapeTo)),
|
|
63
|
+
tabletLandscapeTo: (max-width: map.get($breakpointsVariables, tabletLandscapeTo)),
|
|
64
|
+
desktopFrom: (min-width: map.get($breakpointsVariables, desktopFrom)),
|
|
65
|
+
desktop: $mediaqueryDesktop,
|
|
66
|
+
desktopTo: (max-width: map.get($breakpointsVariables, desktopTo)),
|
|
67
|
+
big: (min-width: map.get($breakpointsVariables, bigFrom)),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// Font
|
|
72
|
+
@mixin font($layout, $fontSize, $lineHeight, $bold:'false') {
|
|
73
|
+
|
|
74
|
+
font-size: size($layout, $fontSize);
|
|
75
|
+
line-height: size($layout, $lineHeight);
|
|
76
|
+
|
|
77
|
+
@if ($bold != 'false') {
|
|
78
|
+
font-weight: $bold;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
// columns
|
|
85
|
+
// Calculates the width (as a fraction of 100%)
|
|
86
|
+
// Per default the function assumes that the given space does not contain column gaps;
|
|
87
|
+
// with the variable $columnGapsAddedBasis they can be specified.
|
|
88
|
+
// $columnGapsAddedBasis is to be used when the space to be calculated contains additional column gaps outside the $columnsBasis.
|
|
89
|
+
// $columnGapsAdditional is to be used when additionally to the $columns column gaps are to be added (e.g left and/or right).
|
|
90
|
+
// $shift is for adding a value to the result, for example a margin
|
|
91
|
+
// $inverse is for calculating the width as a negative number, for example for a negative shift in a stage system
|
|
92
|
+
@function columns($layout, $columns, $columnsBasis, $columnGapsAddedBasis:0, $columnGapsAdditional:0, $margin:0, $shift:0, $invers:false) {
|
|
93
|
+
$columnGap: size($layout, map.get(config.$gutter, $layout) );
|
|
94
|
+
$margin: size($layout, $margin);
|
|
95
|
+
$shift: size($layout, $shift);
|
|
96
|
+
@if ( $invers == false ) {
|
|
97
|
+
$invers: 1;
|
|
98
|
+
} @else {
|
|
99
|
+
$invers: -1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// available space
|
|
103
|
+
// minus column gaps within the space
|
|
104
|
+
// equals space for content
|
|
105
|
+
// divided by number of columns in space
|
|
106
|
+
// equals width of each column
|
|
107
|
+
// times number of desired columns
|
|
108
|
+
// equals width of desired columns
|
|
109
|
+
// plus column gaps between desired columns
|
|
110
|
+
// equals width of desired columns including gaps
|
|
111
|
+
// plus additional column gaps
|
|
112
|
+
@return calc(
|
|
113
|
+
(
|
|
114
|
+
(
|
|
115
|
+
// available space
|
|
116
|
+
100%
|
|
117
|
+
// all column gaps in the given space
|
|
118
|
+
- $margin - $columnGap * ($columnsBasis + $columnGapsAddedBasis - 1)
|
|
119
|
+
)
|
|
120
|
+
// number of columns in the given space
|
|
121
|
+
/ $columnsBasis
|
|
122
|
+
// desired columns
|
|
123
|
+
* ($columns * $invers)
|
|
124
|
+
// columns gaps between desired columns plus additional column gaps
|
|
125
|
+
// inverse to get a negative value
|
|
126
|
+
+ ($columnGap * ($columns - 1 + $columnGapsAdditional) * $invers)
|
|
127
|
+
// Shift
|
|
128
|
+
+ ($shift * $invers)
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
// Size
|
|
136
|
+
// converts a number to a size in px or vw
|
|
137
|
+
// depending on the unit set for the layout
|
|
138
|
+
// accepts single value or a list of values
|
|
139
|
+
@function size($layout, $numbers) {
|
|
140
|
+
// Check whether second argument is a number or a list
|
|
141
|
+
@if (meta.type-of($numbers) == 'list') {
|
|
142
|
+
$result-list: (); // Empty list to store results
|
|
143
|
+
|
|
144
|
+
// Iterate over each element in the list
|
|
145
|
+
@each $number in $numbers {
|
|
146
|
+
// Test if the current element is a number
|
|
147
|
+
@if (meta.type-of($number) == 'number') {
|
|
148
|
+
// add calculated value to the result list
|
|
149
|
+
$result-list: list.append($result-list, _calculate-single-size($number, $layout));
|
|
150
|
+
} @else {
|
|
151
|
+
// Error if the current element is not a number
|
|
152
|
+
@error "Listenelement '#{$number}' übergeben an size() ist keine Zahl. Alle Elemente müssen Zahlen sein.";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
@return $result-list; // Return the list of calculated sizes
|
|
156
|
+
} @else if (meta.type-of($numbers) == 'number') {
|
|
157
|
+
// If second argument is a single number, calculate and return the size
|
|
158
|
+
@return _calculate-single-size($numbers, $layout);
|
|
159
|
+
} @else {
|
|
160
|
+
// Error if the second argument is neither a number nor a list
|
|
161
|
+
@error "Das zweite Argument der Funktion size() muss eine Zahl oder eine Liste von Zahlen sein. Empfangen: #{type-of($numbers)} '#{$numbers}'.";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Round a number to specified decimal places
|
|
166
|
+
@function _decimal-round($number, $digits: 0) {
|
|
167
|
+
$n: math.pow(10, $digits);
|
|
168
|
+
@return math.div(math.round($number * $n), $n);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Helper function to calculate a single size value (px or vw)
|
|
172
|
+
// This function is used by the main size() function
|
|
173
|
+
@function _calculate-single-size($number, $layout) {
|
|
174
|
+
// Get the width and unit type from the config
|
|
175
|
+
$width: map.get(config.$layouts, $layout);
|
|
176
|
+
$unit-type: map.get(config.$units, $layout);
|
|
177
|
+
|
|
178
|
+
// Error handling for missing width or unit type
|
|
179
|
+
@if ($width == null) {
|
|
180
|
+
@error "Layout '#{$layout}' ist nicht in 'config.$layouts' definiert.";
|
|
181
|
+
}
|
|
182
|
+
@if ($unit-type == null) {
|
|
183
|
+
@error "Der Einheitentyp für Layout '#{$layout}' ist nicht in 'config.$units' definiert.";
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@if ($unit-type == 'px') {
|
|
188
|
+
@return $number * 1px;
|
|
189
|
+
} @else if ($unit-type == 'vw') {
|
|
190
|
+
@return _decimal-round(math.div($number, $width) * 100, 2) * 1vw;
|
|
191
|
+
} @else {
|
|
192
|
+
@error "Nicht unterstützter Einheitstyp '#{$unit-type}' für Layout '#{$layout}'. Nur 'px' oder 'vw' werden unterstützt.";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
// Distance
|
|
198
|
+
@mixin marginPadding($layout, $mode, $position, $top: 0, $right: 0, $bottom: 0, $left: 0) {
|
|
199
|
+
|
|
200
|
+
// @include mode($layout, padding/margin, all/top/right/bottom/left, 0, 0, 0, 0)
|
|
201
|
+
|
|
202
|
+
@if ( map.get(config.$units, $layout) == 'px' ) {
|
|
203
|
+
$top: $top + px;
|
|
204
|
+
$right: $right + px;
|
|
205
|
+
$bottom: $bottom + px;
|
|
206
|
+
$left: $left + px;
|
|
207
|
+
} @else {
|
|
208
|
+
|
|
209
|
+
@if meta.type-of($top) == 'number'{
|
|
210
|
+
$top: size($layout, $top);
|
|
211
|
+
}
|
|
212
|
+
@if meta.type-of($right) == 'number'{
|
|
213
|
+
$right: size($layout, $right);
|
|
214
|
+
}
|
|
215
|
+
@if meta.type-of($bottom) == 'number'{
|
|
216
|
+
$bottom: size($layout, $bottom);
|
|
217
|
+
}
|
|
218
|
+
@if meta.type-of($left) == 'number'{
|
|
219
|
+
$left: size($layout, $left);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@if $position == all {
|
|
224
|
+
#{$mode}: $top $right $bottom $left;
|
|
225
|
+
}
|
|
226
|
+
@if $position == top {
|
|
227
|
+
#{$mode}-top: $top;
|
|
228
|
+
}
|
|
229
|
+
@if $position == right {
|
|
230
|
+
#{$mode}-right: $top;
|
|
231
|
+
}
|
|
232
|
+
@if $position == bottom {
|
|
233
|
+
#{$mode}-bottom: $top;
|
|
234
|
+
}
|
|
235
|
+
@if $position == left {
|
|
236
|
+
#{$mode}-left: $top;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
@use "layout";
|
|
2
|
+
|
|
3
|
+
@use "sass:map";
|
|
4
|
+
@use "sass:list";
|
|
5
|
+
|
|
6
|
+
// Zusammenlegen aller Meda Queries in eine
|
|
7
|
+
// https://www.sitepoint.com/sass-mixin-media-merging
|
|
8
|
+
|
|
9
|
+
@mixin mediaquery($mediaquery) {
|
|
10
|
+
// Hole die map $mediaquery aus der map $mediaqueries
|
|
11
|
+
$queries: map.get(layout.$mediaqueries, $mediaquery);
|
|
12
|
+
|
|
13
|
+
// Wenn es keine $mediaquery in der map gibt, gebe einen Fehler aus
|
|
14
|
+
@if not $queries {
|
|
15
|
+
@error "No value could be retrieved from `#{$mediaquery}`. "
|
|
16
|
+
+ "Please make sure it is defined in `$mediaqueries` map.";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Include the media mixin with $queries
|
|
20
|
+
@include media($queries) {
|
|
21
|
+
@content($mediaquery);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@mixin media($queries) {
|
|
26
|
+
// Erst wenn alle mediaqueries geschachtelt geschrieben sind,
|
|
27
|
+
// wird der @content ausgegeben
|
|
28
|
+
@if list.length($queries) == 0 {
|
|
29
|
+
@content;
|
|
30
|
+
} @else {
|
|
31
|
+
$first-key: list.nth(map.keys($queries), 1);
|
|
32
|
+
|
|
33
|
+
// Schreibt nur die mediaquery, nicht den Inhalt
|
|
34
|
+
@media ($first-key: map.get($queries, $first-key)) {
|
|
35
|
+
$queries: map.remove($queries, $first-key);
|
|
36
|
+
|
|
37
|
+
@include media($queries) {
|
|
38
|
+
@content;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* after scroll set body attribute data-scrolled to true
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class BodyScrolled {
|
|
6
|
+
static #instance;
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
document.addEventListener('scroll', function setScrolled() {
|
|
10
|
+
document.body.setAttribute('data-body-scrolled', 'true');
|
|
11
|
+
this.removeEventListener('scroll', setScrolled);
|
|
12
|
+
|
|
13
|
+
// create event
|
|
14
|
+
let event = new CustomEvent('eventBodyScrolled', {
|
|
15
|
+
detail: {
|
|
16
|
+
scrolled: true
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
// dispatch the event
|
|
20
|
+
window.dispatchEvent(event);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static getInstance() {
|
|
26
|
+
if (!BodyScrolled.#instance) {
|
|
27
|
+
BodyScrolled.#instance = new BodyScrolled();
|
|
28
|
+
}
|
|
29
|
+
return BodyScrolled.#instance;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**Media queries
|
|
2
|
+
* https://kinsta.com/blog/javamediaqueryipt-media-query/
|
|
3
|
+
* option 3 on the linked page
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class MediaQueries {
|
|
7
|
+
static #instance;
|
|
8
|
+
layout = 'desktop';
|
|
9
|
+
#breakpoints;
|
|
10
|
+
|
|
11
|
+
constructor(breakpoints) {
|
|
12
|
+
this.#breakpoints = breakpoints;
|
|
13
|
+
this.layout = 'desktop';
|
|
14
|
+
this.changeLayout();
|
|
15
|
+
this.#matchmedia();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static getInstance(breakpoints) {
|
|
19
|
+
if (!MediaQueries.#instance) {
|
|
20
|
+
if (!breakpoints) {
|
|
21
|
+
throw new Error("MediaQueries muss beim ersten Aufruf mit breakpoints initialisiert werden.");
|
|
22
|
+
}
|
|
23
|
+
MediaQueries.#instance = new MediaQueries(breakpoints);
|
|
24
|
+
}
|
|
25
|
+
return MediaQueries.#instance;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#matchmedia() {
|
|
29
|
+
for (let [layout, minSize] of Object.entries(this.#breakpoints)) {
|
|
30
|
+
if (minSize) {
|
|
31
|
+
var matchmedia = window.matchMedia('(min-width: ' + minSize + 'px)');
|
|
32
|
+
matchmedia.addEventListener('change', (e) => this.changeLayout());
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// media query handler function
|
|
38
|
+
changeLayout() {
|
|
39
|
+
for (let [layout, minSize] of Object.entries(this.#breakpoints)) {
|
|
40
|
+
var matchmedia = window.matchMedia('(min-width: ' + minSize + 'px)');
|
|
41
|
+
if (!matchmedia || matchmedia.matches) {
|
|
42
|
+
this.layout = layout;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
document.body.setAttribute('data-layout', this.layout);
|
|
46
|
+
|
|
47
|
+
// create event
|
|
48
|
+
let event = new CustomEvent('eventLayoutchange', {
|
|
49
|
+
detail: {
|
|
50
|
+
layout: this.layout
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
// dispatch the event
|
|
54
|
+
window.dispatchEvent(event);
|
|
55
|
+
}
|
|
56
|
+
}
|
package/utils/Vh100.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 100vh problem
|
|
3
|
+
* https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class Vh100 {
|
|
7
|
+
static #instance;
|
|
8
|
+
vh = 0;
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
this.#calculate();
|
|
12
|
+
// We listen to the resize event
|
|
13
|
+
window.addEventListener('resize', () => {
|
|
14
|
+
this.#calculate();
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#calculate() {
|
|
19
|
+
// First we get the viewport height and we multiple it by 1% to get a value for a vh unit
|
|
20
|
+
this.vh = window.innerHeight * 0.01;
|
|
21
|
+
// Then we set the value in the --vh custom property to the root of the document
|
|
22
|
+
document.documentElement.style.setProperty('--vh', `${this.vh}px`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static getInstance() {
|
|
26
|
+
if (!Vh100.#instance) {
|
|
27
|
+
Vh100.#instance = new Vh100();
|
|
28
|
+
}
|
|
29
|
+
return Vh100.#instance;
|
|
30
|
+
}
|
|
31
|
+
}
|