@flitsmeister/design-system 1.0.1
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/.github/workflows/npm-publish.yml +34 -0
- package/README.md +3 -0
- package/package.json +65 -0
- package/postcss.config.js +20 -0
- package/svgo.config.js +37 -0
- package/tailwind.config.js +146 -0
- package/web/.eslintrc +5 -0
- package/web/App.vue +60 -0
- package/web/components/CookieConsent.vue +115 -0
- package/web/components/Flag.vue +47 -0
- package/web/components/Icon.vue +31 -0
- package/web/components/LanguageSwitcher.vue +77 -0
- package/web/components/PermissionToggle.vue +49 -0
- package/web/components/button-with-loader.vue +26 -0
- package/web/components/fmxButton.vue +225 -0
- package/web/components/fmxInput.vue +133 -0
- package/web/components/loader.vue +22 -0
- package/web/components/modal.vue +61 -0
- package/web/components/navbar.vue +69 -0
- package/web/icons/bold/add.svg +3 -0
- package/web/icons/bold/check.svg +3 -0
- package/web/icons/bold/checkbox-checked.svg +4 -0
- package/web/icons/bold/checkbox-unchecked.svg +3 -0
- package/web/icons/bold/chevron-backward.svg +3 -0
- package/web/icons/bold/chevron-down.svg +3 -0
- package/web/icons/bold/chevron-forward.svg +3 -0
- package/web/icons/bold/chevron-up.svg +3 -0
- package/web/icons/bold/close-1.svg +3 -0
- package/web/icons/bold/close.svg +4 -0
- package/web/icons/bold/dot.svg +3 -0
- package/web/icons/bold/open-external.svg +3 -0
- package/web/icons/bold/prominent-1.svg +3 -0
- package/web/icons/bold/prominent.svg +3 -0
- package/web/icons/bold/question.svg +4 -0
- package/web/icons/bold/remove.svg +3 -0
- package/web/icons/bold/search.svg +3 -0
- package/web/icons/bold/spinner.svg +3 -0
- package/web/icons/bold/warning.svg +3 -0
- package/web/icons/duotone/check.svg +4 -0
- package/web/icons/filled/achievements.svg +3 -0
- package/web/icons/filled/add.svg +3 -0
- package/web/icons/filled/app-settings.svg +3 -0
- package/web/icons/filled/calendar.svg +7 -0
- package/web/icons/filled/car-simplified.svg +3 -0
- package/web/icons/filled/check.svg +3 -0
- package/web/icons/filled/clock.svg +3 -0
- package/web/icons/filled/close.svg +3 -0
- package/web/icons/filled/control.svg +7 -0
- package/web/icons/filled/danger.svg +7 -0
- package/web/icons/filled/delete.svg +3 -0
- package/web/icons/filled/deviceOne.svg +3 -0
- package/web/icons/filled/deviceTwo.svg +3 -0
- package/web/icons/filled/disputeFine.svg +3 -0
- package/web/icons/filled/email.svg +3 -0
- package/web/icons/filled/gasPrices.svg +3 -0
- package/web/icons/filled/hazard.svg +3 -0
- package/web/icons/filled/highway.svg +3 -0
- package/web/icons/filled/info.svg +4 -0
- package/web/icons/filled/lock-close.svg +3 -0
- package/web/icons/filled/lock-open.svg +3 -0
- package/web/icons/filled/lock.svg +3 -0
- package/web/icons/filled/microphone.svg +4 -0
- package/web/icons/filled/notificaiton-unread.svg +4 -0
- package/web/icons/filled/notificaiton.svg +3 -0
- package/web/icons/filled/parking.svg +3 -0
- package/web/icons/filled/play.svg +8 -0
- package/web/icons/filled/question.svg +3 -0
- package/web/icons/filled/rating-empty.svg +3 -0
- package/web/icons/filled/rating-full.svg +3 -0
- package/web/icons/filled/rating-half.svg +4 -0
- package/web/icons/filled/remove.svg +3 -0
- package/web/icons/filled/return-location.svg +3 -0
- package/web/icons/filled/support.svg +3 -0
- package/web/icons/filled/toll.svg +3 -0
- package/web/icons/filled/tripHistory.svg +3 -0
- package/web/icons/filled/truck.svg +3 -0
- package/web/icons/filled/user-pin.svg +3 -0
- package/web/icons/filled/user-profile.svg +3 -0
- package/web/icons/filled/warning.svg +4 -0
- package/web/icons/filled/zone-default.svg +3 -0
- package/web/icons/filled/zone-indoor.svg +3 -0
- package/web/icons/filled/zone-street.svg +3 -0
- package/web/icons/stroke/arrow-Down.svg +3 -0
- package/web/icons/stroke/arrow-Up.svg +3 -0
- package/web/icons/stroke/arrow-left.svg +3 -0
- package/web/icons/stroke/arrow-right.svg +3 -0
- package/web/icons/stroke/check.svg +3 -0
- package/web/icons/stroke/checkbox-checked.svg +4 -0
- package/web/icons/stroke/checkbox-unchecked.svg +3 -0
- package/web/icons/stroke/chevron-backward.svg +3 -0
- package/web/icons/stroke/chevron-down.svg +3 -0
- package/web/icons/stroke/chevron-forward.svg +3 -0
- package/web/icons/stroke/chevron-up.svg +3 -0
- package/web/icons/stroke/clock.svg +3 -0
- package/web/icons/stroke/close-1.svg +3 -0
- package/web/icons/stroke/close.svg +4 -0
- package/web/icons/stroke/download.svg +4 -0
- package/web/icons/stroke/email.svg +4 -0
- package/web/icons/stroke/hamburger.svg +3 -0
- package/web/icons/stroke/login.svg +4 -0
- package/web/icons/stroke/logout.svg +3 -0
- package/web/icons/stroke/more.svg +5 -0
- package/web/icons/stroke/open-external.svg +3 -0
- package/web/icons/stroke/question.svg +4 -0
- package/web/icons/stroke/search.svg +3 -0
- package/web/icons/stroke/spinner.svg +3 -0
- package/web/icons/stroke/warning.svg +3 -0
- package/web/index.html +25 -0
- package/web/index.js +55 -0
- package/web/locales/da.json +197 -0
- package/web/locales/de.json +197 -0
- package/web/locales/en.json +333 -0
- package/web/locales/fi.json +197 -0
- package/web/locales/fr.json +197 -0
- package/web/locales/nl.json +333 -0
- package/web/locales/no.json +197 -0
- package/web/locales/pl.json +197 -0
- package/web/locales/sv.json +197 -0
- package/web/pages/buttons.html +32 -0
- package/web/pages/buttons.vue +18 -0
- package/web/pages/colors.html +32 -0
- package/web/pages/colors.vue +29 -0
- package/web/pages/icons.vue +49 -0
- package/web/pages/index.html +25 -0
- package/web/pages/index.vue +15 -0
- package/web/pages/input.html +47 -0
- package/web/pages/input.vue +34 -0
- package/web/router.js +20 -0
- package/web/static/apple-touch-icon.png +0 -0
- package/web/static/download.png +0 -0
- package/web/static/facebook-login.png +0 -0
- package/web/static/facebook-migrate.png +0 -0
- package/web/static/favicon-16x16.png +0 -0
- package/web/static/favicon-32x32.png +0 -0
- package/web/static/favicon.ico +0 -0
- package/web/static/fleet-bg.png +0 -0
- package/web/static/flitsmeister-logo-small.png +0 -0
- package/web/static/flitsmeister-logo.png +0 -0
- package/web/static/flitsmeister_app_icon.png +0 -0
- package/web/static/fm-dash-logo.svg +19 -0
- package/web/static/icon-facebook.png +0 -0
- package/web/static/index.png +0 -0
- package/web/static/login-bg.png +0 -0
- package/web/static/no_invoices.png +0 -0
- package/web/static/person-shrugging.png +0 -0
- package/web/static/pro_confetti@2x.png +0 -0
- package/web/static/pro_star@2x.png +0 -0
- package/web/static/sso/bmfleet.png +0 -0
- package/web/static/sso/pitstop.png +0 -0
- package/web/static/sso/truckmeister.png +0 -0
- package/web/static/sso/webshop.png +0 -0
- package/web/static/triplogging_whatsnew.png +0 -0
- package/web/store.js +79 -0
- package/web/styles/app.scss +9 -0
- package/web/styles/flashmaster/fm-design-system.json +4414 -0
- package/web/styles/flashmaster/primitives.json +604 -0
- package/web/styles/flashmaster.scss +162 -0
- package/webpack.config.js +161 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 22
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm run build
|
|
20
|
+
|
|
21
|
+
publish-npm:
|
|
22
|
+
needs: build
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 22
|
|
29
|
+
registry-url: https://registry.npmjs.org/
|
|
30
|
+
- run: npm ci
|
|
31
|
+
- run: npm run build
|
|
32
|
+
- run: npm publish
|
|
33
|
+
env:
|
|
34
|
+
NPM_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
|
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flitsmeister/design-system",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Flitsmeister design system and demo site",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "MODE=development webpack serve",
|
|
8
|
+
"build": "MODE=production webpack",
|
|
9
|
+
"build-dev": "MODE=development webpack",
|
|
10
|
+
"test": "exit 0;"
|
|
11
|
+
},
|
|
12
|
+
"author": "Flitsmeister",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@fullhuman/postcss-purgecss": "^5.0.0",
|
|
16
|
+
"@intlify/vue-i18n-loader": "~4.2.0",
|
|
17
|
+
"autoprefixer": "~10.4.13",
|
|
18
|
+
"axios": "~1.3.2",
|
|
19
|
+
"clean-webpack-plugin": "~4.0.0",
|
|
20
|
+
"copy-webpack-plugin": "~11.0.0",
|
|
21
|
+
"css-loader": "~6.7.3",
|
|
22
|
+
"dotenv-webpack": "~8.0.1",
|
|
23
|
+
"es6-promise": "~4.2.8",
|
|
24
|
+
"flagpack-core": "^2.0.0",
|
|
25
|
+
"html-webpack-plugin": "~5.5.0",
|
|
26
|
+
"node-sass": "~8.0.0",
|
|
27
|
+
"postcss": "~8.4.21",
|
|
28
|
+
"postcss-loader": "~7.0.2",
|
|
29
|
+
"sass-loader": "~13.2.0",
|
|
30
|
+
"style-loader": "~3.3.1",
|
|
31
|
+
"tailwindcss": "~3.2.6",
|
|
32
|
+
"uuid": "~9.0.0",
|
|
33
|
+
"vue": "~3.2.47",
|
|
34
|
+
"vue-i18n": "~9.2.2",
|
|
35
|
+
"vue-loader": "~17.0.1",
|
|
36
|
+
"vue-router": "~4.1.6",
|
|
37
|
+
"vue-style-loader": "~4.1.3",
|
|
38
|
+
"vue-template-compiler": "~2.7.14",
|
|
39
|
+
"vuex": "~4.1.0",
|
|
40
|
+
"webpack": "~5.75.0",
|
|
41
|
+
"webpack-cli": "~5.0.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@flitsmeister/eslint": "~1.2.1",
|
|
45
|
+
"svg-sprite-loader": "~6.0.11",
|
|
46
|
+
"svgo": "~3.3.2",
|
|
47
|
+
"svgo-loader": "~4.0.0",
|
|
48
|
+
"webpack-dev-server": "~4.11.1"
|
|
49
|
+
},
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/flitsmeister/design-system.git"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"flitsmeister",
|
|
56
|
+
"design",
|
|
57
|
+
"styling",
|
|
58
|
+
"colors",
|
|
59
|
+
"icons"
|
|
60
|
+
],
|
|
61
|
+
"bugs": {
|
|
62
|
+
"url": "https://github.com/flitsmeister/design-system/issues"
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/flitsmeister/design-system#readme"
|
|
65
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const Autoprefixer = require('autoprefixer')
|
|
2
|
+
const TailwindCSS = require('tailwindcss')
|
|
3
|
+
const PostCSSPurgeCSS = require('@fullhuman/postcss-purgecss')
|
|
4
|
+
|
|
5
|
+
const purgecss = PostCSSPurgeCSS({
|
|
6
|
+
content: ['./web/**/*.html', './web/**/*.vue', './web/**/*.js'],
|
|
7
|
+
// Include any special characters you're using in this regular expression.
|
|
8
|
+
// See: https://tailwindcss.com/docs/controlling-file-size/#understanding-the-regex
|
|
9
|
+
defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
|
|
10
|
+
// Whitelist auto generated classes for transitions and router links.
|
|
11
|
+
// From: https://github.com/ky-is/vue-cli-plugin-tailwind
|
|
12
|
+
whitelistPatterns: [/-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/]
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
plugins: [
|
|
17
|
+
TailwindCSS,
|
|
18
|
+
Autoprefixer,
|
|
19
|
+
...(process.env.NODE_ENV === 'production' ? [purgecss] : [])]
|
|
20
|
+
}
|
package/svgo.config.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* eslint-disable strict */
|
|
2
|
+
module.exports = {
|
|
3
|
+
plugins: [
|
|
4
|
+
{
|
|
5
|
+
name: 'preset-default',
|
|
6
|
+
params: {
|
|
7
|
+
overrides: {
|
|
8
|
+
// disable plugins
|
|
9
|
+
removeViewBox: false,
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/* trial attempt at replacing fill and stroke with currentColor - this does work but requires some additional work
|
|
14
|
+
{
|
|
15
|
+
name: 'customReplaceFillAndStroke',
|
|
16
|
+
type: 'perItem',
|
|
17
|
+
fn: (node) => {
|
|
18
|
+
console.log('Processing SVGO customReplace ', node);
|
|
19
|
+
if (node.type === 'element') {
|
|
20
|
+
node.attributes = Object.fromEntries(
|
|
21
|
+
Object.entries(node.attributes).map(([name, value]) => {
|
|
22
|
+
if (
|
|
23
|
+
(name === 'fill' || name === 'stroke') &&
|
|
24
|
+
(value === 'black' || value === '#161616')
|
|
25
|
+
) {
|
|
26
|
+
console.log('Replacing', name, value, 'with currentColor');
|
|
27
|
+
return [name, 'currentColor'];
|
|
28
|
+
}
|
|
29
|
+
return [name, value];
|
|
30
|
+
})
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
*/
|
|
36
|
+
]
|
|
37
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/* eslint-disable strict */
|
|
2
|
+
const colors = require('tailwindcss/colors')
|
|
3
|
+
const importedPrimitives = require('./web/styles/flashmaster/primitives.json')
|
|
4
|
+
|
|
5
|
+
if (!importedPrimitives[0].Primitives.modes.default.color) {
|
|
6
|
+
throw new Error('Primitives not found in tailwind.config.js')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const primitives = importedPrimitives[0].Primitives.modes.default.color
|
|
10
|
+
|
|
11
|
+
const mapPrimitivesToTailwindColors = (primitivesData) => {
|
|
12
|
+
const tailwindColors = {}
|
|
13
|
+
|
|
14
|
+
for (const [colorName, shades] of Object.entries(primitivesData)) {
|
|
15
|
+
tailwindColors[colorName.toLowerCase()] = {}
|
|
16
|
+
|
|
17
|
+
for (const [shadeName, shadeValue] of Object.entries(shades)) {
|
|
18
|
+
const match = shadeName.match(/\d+/) // Extract the number from the shade name
|
|
19
|
+
if (match) {
|
|
20
|
+
const shadeNumber = match[0]
|
|
21
|
+
tailwindColors[colorName.toLowerCase()][shadeNumber] = shadeValue.$value
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return tailwindColors
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const customColors = mapPrimitivesToTailwindColors(primitives)
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
content: ['./web/**/*.html', './web/**/*.vue', './web/**/*.jsx', './web/**/*.js'],
|
|
33
|
+
corePlugins: {
|
|
34
|
+
float: false
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/* NOT REQUIRED FOR FLASHMASTER: darkMode is only being set to 'class' for this demo site */
|
|
38
|
+
darkMode: 'class',
|
|
39
|
+
|
|
40
|
+
/* REQUIRED FOR FLASHMASTER */
|
|
41
|
+
theme: {
|
|
42
|
+
container: {
|
|
43
|
+
center: true
|
|
44
|
+
},
|
|
45
|
+
colors: {
|
|
46
|
+
inherit: colors.inherit,
|
|
47
|
+
current: colors.current,
|
|
48
|
+
transparent: colors.transparent,
|
|
49
|
+
black: colors.black,
|
|
50
|
+
white: colors.white,
|
|
51
|
+
...customColors, // Add custom colors from primitives.json
|
|
52
|
+
// designsystem token colors
|
|
53
|
+
surface: {
|
|
54
|
+
defaulthighest: 'var(--surface-defaultHighest)',
|
|
55
|
+
defaulthigher: 'var(--surface-defaultHigher)',
|
|
56
|
+
default: 'var(--surface-default)',
|
|
57
|
+
defaultlower: 'var(--surface-defaultLower)',
|
|
58
|
+
defaultlowest: 'var(--surface-defaultLowest)',
|
|
59
|
+
brand: 'var(--surface-brand)',
|
|
60
|
+
brandalt: 'var(--surface-brandAlt)',
|
|
61
|
+
pro: 'var(--surface-pro)',
|
|
62
|
+
proalt: 'var(--surface-proAlt)',
|
|
63
|
+
proplus: 'var(--surface-proPlus)',
|
|
64
|
+
danger: 'var(--surface-danger)',
|
|
65
|
+
dangeralt: 'var(--surface-dangerAlt)',
|
|
66
|
+
warning: 'var(--surface-warning)',
|
|
67
|
+
warningalt: 'var(--surface-warningAlt)',
|
|
68
|
+
attention: 'var(--surface-attention)',
|
|
69
|
+
info: 'var(--surface-info)',
|
|
70
|
+
infoalt: 'var(--surface-infoAlt)',
|
|
71
|
+
success: 'var(--surface-success)',
|
|
72
|
+
successalt: 'var(--surface-successAlt)'
|
|
73
|
+
},
|
|
74
|
+
content: {
|
|
75
|
+
default: 'var(--content-default)',
|
|
76
|
+
defaultalt: 'var(--content-defaultAlt)',
|
|
77
|
+
weak: 'var(--content-weak)',
|
|
78
|
+
label: 'var(--content-label)',
|
|
79
|
+
brand: 'var(--content-brand)',
|
|
80
|
+
pro: 'var(--content-pro)',
|
|
81
|
+
proplus: 'var(--content-proPlus)',
|
|
82
|
+
danger: 'var(--content-danger)',
|
|
83
|
+
warning: 'var(--content-warning)',
|
|
84
|
+
attention: 'var(--content-attention)',
|
|
85
|
+
info: 'var(--content-info)',
|
|
86
|
+
success: 'var(--content-success)'
|
|
87
|
+
},
|
|
88
|
+
outline: {
|
|
89
|
+
default: 'var(--outline-default)',
|
|
90
|
+
brand: 'var(--outline-brand)',
|
|
91
|
+
danger: 'var(--outline-danger)',
|
|
92
|
+
dangeralt: 'var(--outline-dangerAlt)',
|
|
93
|
+
warning: 'var(--outline-warning)',
|
|
94
|
+
warningalt: 'var(--outline-warningAlt)',
|
|
95
|
+
info: 'var(--outline-info)',
|
|
96
|
+
success: 'var(--outline-success)'
|
|
97
|
+
},
|
|
98
|
+
feature: {
|
|
99
|
+
speedcam: 'var(--feature-speedcam)',
|
|
100
|
+
speedcheck: 'var(--feature-speedcheck)',
|
|
101
|
+
parking: 'var(--feature-parking)',
|
|
102
|
+
parkingalt: 'var(--feature-parkingAlt)',
|
|
103
|
+
police: 'var(--feature-police)',
|
|
104
|
+
firetruck: 'var(--feature-firetruck)',
|
|
105
|
+
ambulance: 'var(--feature-ambulance)',
|
|
106
|
+
emergency: 'var(--feature-emergency)',
|
|
107
|
+
signal: 'var(--feature-signal)'
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
extend: {
|
|
111
|
+
spacing: {
|
|
112
|
+
/*
|
|
113
|
+
1 - nudge / Tailwind "px"
|
|
114
|
+
2 - xxxs / Tailwind "0.5"
|
|
115
|
+
4 - xxs / Tailwind "1"
|
|
116
|
+
8 - xs / Tailwind "2"
|
|
117
|
+
12 - sm / Tailwind "3"
|
|
118
|
+
16 - md / Tailwind "4"
|
|
119
|
+
24 - lg / Tailwind "6"
|
|
120
|
+
32 - xl / Tailwind "8"
|
|
121
|
+
48 - xxl / Tailwind "12"
|
|
122
|
+
64 - xxxl / Tailwind "16"
|
|
123
|
+
*/
|
|
124
|
+
nudge: '1px',
|
|
125
|
+
xxxs: '2px',
|
|
126
|
+
xxs: '4px',
|
|
127
|
+
xs: '8px',
|
|
128
|
+
sm: '12px',
|
|
129
|
+
md: '16px',
|
|
130
|
+
lg: '24px',
|
|
131
|
+
xl: '32px',
|
|
132
|
+
xxl: '48px',
|
|
133
|
+
xxxl: '64px'
|
|
134
|
+
},
|
|
135
|
+
fontSize: {
|
|
136
|
+
// the other fontsize tokens (xs, sm, md, lg & xl) already happen to match with those of Tailwind :D
|
|
137
|
+
// in hindsight, it seems that these are barely even used...
|
|
138
|
+
xxl: '2.5rem', // 40px
|
|
139
|
+
xxxl: '3.5', // 56px
|
|
140
|
+
xxxxl: '4rem' // 64px
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
variants: {},
|
|
145
|
+
plugins: []
|
|
146
|
+
}
|
package/web/.eslintrc
ADDED
package/web/App.vue
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="transition-all duration-300 min-h-screen bg-surface-default text-content-default">
|
|
3
|
+
<Navbar />
|
|
4
|
+
<div id="navtarget">
|
|
5
|
+
<router-view />
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
<div id="iconsprite" v-html="spriteContent"></div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
import Navbar from './components/navbar.vue'
|
|
13
|
+
|
|
14
|
+
// Import all SVG files in the icons directory
|
|
15
|
+
const requireAll = (requireContext) => requireContext.keys().map(requireContext);
|
|
16
|
+
const req = require.context('./icons', true, /\.svg$/);
|
|
17
|
+
requireAll(req);
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
name: 'flitsmeister-theme',
|
|
21
|
+
components: { Navbar },
|
|
22
|
+
data() {
|
|
23
|
+
return {
|
|
24
|
+
spriteContent: ''
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
mounted() {
|
|
28
|
+
fetch('/sprite.svg')
|
|
29
|
+
.then(response => response.text())
|
|
30
|
+
.then(sprite => {
|
|
31
|
+
this.spriteContent = sprite;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<style src="./styles/app.scss" lang="scss" />
|
|
38
|
+
<style lang="scss" module>
|
|
39
|
+
#app {
|
|
40
|
+
min-height: 100%;
|
|
41
|
+
height: 100%;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#iconsprite {
|
|
45
|
+
display: none;
|
|
46
|
+
|
|
47
|
+
// override fill and/or stroke color, only when it's already set
|
|
48
|
+
symbol:not([id^="duotone-"]) {
|
|
49
|
+
path, rect, circle, ellipse, polygon, polyline, line {
|
|
50
|
+
&[fill] {
|
|
51
|
+
fill: currentColor;
|
|
52
|
+
}
|
|
53
|
+
&[stroke] {
|
|
54
|
+
stroke: currentColor;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/* might still need to look at 'mask' elements */
|
|
59
|
+
}
|
|
60
|
+
</style>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="cookie-settings z-10 transition-all duration-300 overflow-hidden shadow-lg border border-neutral-100 fixed flex flex-col gap-2 bottom-4 right-4 bg-white max-w-[360px] rounded-2xl px-4 py-3"
|
|
4
|
+
:class="{ 'translate-y-8 opacity-0 pointer-events-none': !settings.display }"
|
|
5
|
+
>
|
|
6
|
+
<span class="text-blue font-semibold">
|
|
7
|
+
{{ $t('cookies.title') }}
|
|
8
|
+
</span>
|
|
9
|
+
<div class="text-gray-800 text-sm font-light">
|
|
10
|
+
{{ $t('cookies.description') }}
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
<div v-if="settings.showAll === false" class="flex items-center gap-2 mt-2 w-full justify-evenly">
|
|
15
|
+
<button class="bg-blue text-sm text-white font-medium py-2 rounded-xl w-full" @click="toggleSetting('display')">
|
|
16
|
+
{{ $t('cookies.refuse') }}
|
|
17
|
+
</button>
|
|
18
|
+
<button class="bg-blue text-sm text-white font-medium py-2 rounded-xl w-full" @click="toggleSetting('showAll')">
|
|
19
|
+
{{ $t('cookies.customize') }}
|
|
20
|
+
</button>
|
|
21
|
+
<button class="bg-blue text-sm text-white font-medium py-2 rounded-xl w-full" @click="saveSettings(true)">
|
|
22
|
+
{{ $t('cookies.accept') }}
|
|
23
|
+
</button>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<Transition name="slide-up">
|
|
27
|
+
<form v-if="settings.showAll" class="flex flex-col gap-2" @submit.prevent="saveSettings">
|
|
28
|
+
<PermissionToggle
|
|
29
|
+
:title="this.$i18n.t('cookies.permissionsections.essentials.title')"
|
|
30
|
+
:description="this.$i18n.t('cookies.permissionsections.essentials.description')"
|
|
31
|
+
:value="settings.essentials"
|
|
32
|
+
:onToggle="() => toggleSetting('essentials')"
|
|
33
|
+
/>
|
|
34
|
+
<PermissionToggle
|
|
35
|
+
:title="this.$i18n.t('cookies.permissionsections.preferences.title')"
|
|
36
|
+
:description="this.$i18n.t('cookies.permissionsections.preferences.description')"
|
|
37
|
+
:value="settings.preferences"
|
|
38
|
+
:onToggle="() => toggleSetting('preferences')"
|
|
39
|
+
/>
|
|
40
|
+
<PermissionToggle
|
|
41
|
+
:title="this.$i18n.t('cookies.permissionsections.analytics.title')"
|
|
42
|
+
:description="this.$i18n.t('cookies.permissionsections.analytics.description')"
|
|
43
|
+
:value="settings.analytics"
|
|
44
|
+
:onToggle="() => toggleSetting('analytics')"
|
|
45
|
+
/>
|
|
46
|
+
<PermissionToggle
|
|
47
|
+
:title="this.$i18n.t('cookies.permissionsections.marketing.title')"
|
|
48
|
+
:description="this.$i18n.t('cookies.permissionsections.marketing.description')"
|
|
49
|
+
:value="settings.marketing"
|
|
50
|
+
:onToggle="() => toggleSetting('marketing')"
|
|
51
|
+
/>
|
|
52
|
+
<button type="submit" class="bg-blue text-white font-semibold py-2 px-4 rounded" @click="() => saveSettings">
|
|
53
|
+
{{ $t('cookies.save') }}
|
|
54
|
+
</button>
|
|
55
|
+
</form>
|
|
56
|
+
</Transition>
|
|
57
|
+
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|
|
60
|
+
|
|
61
|
+
<script>
|
|
62
|
+
import { mapState, mapActions } from 'vuex';
|
|
63
|
+
import PermissionToggle from './PermissionToggle.vue';
|
|
64
|
+
|
|
65
|
+
export default {
|
|
66
|
+
components: {
|
|
67
|
+
PermissionToggle,
|
|
68
|
+
},
|
|
69
|
+
computed: {
|
|
70
|
+
...mapState({
|
|
71
|
+
settings: (state) => state.cookieSettings,
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
methods: {
|
|
75
|
+
...mapActions(['toggleSetting', 'saveCookieSettings']),
|
|
76
|
+
async saveSettings(acceptAll = false) {
|
|
77
|
+
if (acceptAll === true) {
|
|
78
|
+
await this.toggleSetting({key: 'analytics', value: true});
|
|
79
|
+
await this.toggleSetting({key: 'marketing', value: true});
|
|
80
|
+
await this.toggleSetting({key: 'preferences', value: true});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await this.toggleSetting({key: 'display', value: false});
|
|
84
|
+
await this.saveCookieSettings(this.settings);
|
|
85
|
+
this.updateGTMConsent(this.settings);
|
|
86
|
+
},
|
|
87
|
+
updateGTMConsent(settings) {
|
|
88
|
+
gtag('consent', 'update', {
|
|
89
|
+
'functionality_storage': settings.essentials ? 'granted' : 'denied',
|
|
90
|
+
'security_storage': settings.essentials ? 'granted' : 'denied',
|
|
91
|
+
'personalization_storage': settings.preferences ? 'granted' : 'denied',
|
|
92
|
+
'analytics_storage': settings.analytics ? 'granted' : 'denied',
|
|
93
|
+
'ad_storage': settings.marketing ? 'granted' : 'denied',
|
|
94
|
+
'ad_user_data': settings.marketing ? 'granted' : 'denied',
|
|
95
|
+
'ad_personalization': settings.marketing ? 'granted' : 'denied'
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<style scoped>
|
|
103
|
+
.slide-up-enter-active,
|
|
104
|
+
.slide-up-leave-active {
|
|
105
|
+
transition: all 0.5s ease;
|
|
106
|
+
transform-origin: top;
|
|
107
|
+
max-height: 550px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.slide-up-enter-from,
|
|
111
|
+
.slide-up-leave-to {
|
|
112
|
+
transform: translateY(100%);
|
|
113
|
+
max-height: 42px;
|
|
114
|
+
}
|
|
115
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<img :src="flagSrc" :alt="countryCode" :class="sizeClass" v-if="flagSrc" />
|
|
3
|
+
<span v-else class="text-red-500">x</span>
|
|
4
|
+
</template>
|
|
5
|
+
|
|
6
|
+
<script>
|
|
7
|
+
import { isoToCountryCode } from 'flagpack-core';
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
props: {
|
|
11
|
+
countryCode: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: true
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: 'medium',
|
|
18
|
+
validator: value => ['s', 'm', 'l'].includes(value)
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
computed: {
|
|
22
|
+
flagSrc() {
|
|
23
|
+
try {
|
|
24
|
+
const alpha2Code = isoToCountryCode(this.countryCode.toUpperCase(), 'alpha2');
|
|
25
|
+
if (!alpha2Code) {
|
|
26
|
+
throw new Error(`No alpha2 code found for country code: ${this.countryCode}`);
|
|
27
|
+
}
|
|
28
|
+
return require(`flagpack-core/svg/${this.size[0]}/${alpha2Code}.svg`);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(error.message);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
sizeClass() {
|
|
35
|
+
return {
|
|
36
|
+
s: 'w-4 h-3',
|
|
37
|
+
m: 'w-5 h-4',
|
|
38
|
+
l: 'w-8 h-6'
|
|
39
|
+
}[this.size];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<style scoped>
|
|
46
|
+
/* Add any necessary styles here */
|
|
47
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<svg v-if="name" :class="iconClass">
|
|
3
|
+
<use :xlink:href="`#${name}`"></use>
|
|
4
|
+
</svg>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
export default {
|
|
9
|
+
name: 'Icon',
|
|
10
|
+
props: {
|
|
11
|
+
name: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: true
|
|
14
|
+
},
|
|
15
|
+
iconClass: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: ''
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<style scoped>
|
|
24
|
+
svg {
|
|
25
|
+
/* inherit parent element font size and color by default */
|
|
26
|
+
width: 1em;
|
|
27
|
+
height: 1em;
|
|
28
|
+
fill: currentColor;
|
|
29
|
+
display: inline-block;
|
|
30
|
+
}
|
|
31
|
+
</style>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="z-10 fixed top-4 right-4 bg-white px-4 py-3">
|
|
3
|
+
<div class="relative inline-block text-left">
|
|
4
|
+
<div>
|
|
5
|
+
<button @click="toggleDropdown" type="button" :title="languages[currentLanguage].name" class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-2xl text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" id="menu-button" aria-expanded="true" aria-haspopup="true">
|
|
6
|
+
<Flag :countryCode="(languages[currentLanguage].flagiso) ? languages[currentLanguage].flagiso : currentLanguage" size="m" class="inline-block mr-2 my-auto" />
|
|
7
|
+
|
|
8
|
+
<svg class="-mr-1 ml-3 mt-1 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
9
|
+
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
10
|
+
</svg>
|
|
11
|
+
</button>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div v-if="dropdownOpen" class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
|
|
15
|
+
<div class="py-1" role="none">
|
|
16
|
+
<a v-for="(language, key) in languages" :key="key" @click="changeLanguage(key)" :title="language.name" class="text-gray-700 block px-4 py-2 text-sm font-normal cursor-pointer hover:bg-gray-100" role="menuitem" tabindex="-1">
|
|
17
|
+
<Flag :countryCode="(language.flagiso) ? language.flagiso : key" size="s" class="inline-block mr-2" />
|
|
18
|
+
{{ language.name }}
|
|
19
|
+
</a>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
import Flag from './Flag.vue';
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
components: {
|
|
31
|
+
Flag
|
|
32
|
+
},
|
|
33
|
+
data() {
|
|
34
|
+
return {
|
|
35
|
+
currentLanguage: this.$i18n.locale,
|
|
36
|
+
dropdownOpen: false,
|
|
37
|
+
languages: {
|
|
38
|
+
en: { name: 'English', flagiso: 'gb-ukm' },
|
|
39
|
+
da: { name: 'Dansk', flagiso: 'dk' },
|
|
40
|
+
de: { name: 'Deutsch' },
|
|
41
|
+
fi: { name: 'Suomi' },
|
|
42
|
+
fr: { name: 'Français' },
|
|
43
|
+
nl: { name: 'Nederlands' },
|
|
44
|
+
no: { name: 'Norsk' },
|
|
45
|
+
pl: { name: 'Polski' },
|
|
46
|
+
sv: { name: 'Svenska' },
|
|
47
|
+
es: { name: 'Español' }
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
methods: {
|
|
52
|
+
toggleDropdown() {
|
|
53
|
+
this.dropdownOpen = !this.dropdownOpen;
|
|
54
|
+
},
|
|
55
|
+
changeLanguage(languageKey) {
|
|
56
|
+
this.currentLanguage = languageKey;
|
|
57
|
+
this.$i18n.locale = languageKey;
|
|
58
|
+
this.dropdownOpen = false;
|
|
59
|
+
},
|
|
60
|
+
handleClickOutside(event) {
|
|
61
|
+
if (!this.$el.contains(event.target)) {
|
|
62
|
+
this.dropdownOpen = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
mounted() {
|
|
67
|
+
document.addEventListener('click', this.handleClickOutside);
|
|
68
|
+
},
|
|
69
|
+
beforeUnmount() {
|
|
70
|
+
document.removeEventListener('click', this.handleClickOutside);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<style scoped>
|
|
76
|
+
/* Add any necessary styles here */
|
|
77
|
+
</style>
|