eslint
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
name: Lint
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
eslint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install modules
|
||||||
|
run: npm ci
|
||||||
|
- name: Run ESLint
|
||||||
|
run: npm run lint
|
||||||
@@ -1 +1,2 @@
|
|||||||
.idea
|
.idea
|
||||||
|
node_modules
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Meeting Roulette
|
||||||
|
*🎡 Spin your meetings*
|
||||||
|
|
||||||
|
### [Tool link](https://klemek.github.io/meeting-roulette/)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import eslintConfigPrettier from "eslint-config-prettier";
|
||||||
|
import globals from "globals";
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import pluginVue from "eslint-plugin-vue";
|
||||||
|
|
||||||
|
/** @type {import('eslint').Linter.Config[]} */
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
confetti: "readonly",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginJs.configs.all,
|
||||||
|
...pluginVue.configs["flat/recommended"],
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"no-magic-numbers": "off",
|
||||||
|
"sort-keys": "off",
|
||||||
|
"no-warning-comments": "off",
|
||||||
|
"no-ternary": "off",
|
||||||
|
"one-var": "off",
|
||||||
|
"max-statements": ["warn", 50],
|
||||||
|
"max-params": ["warn", 5],
|
||||||
|
"max-lines": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eslintConfigPrettier,
|
||||||
|
];
|
||||||
+10
-8
@@ -3,8 +3,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Meeting Roulette</title>
|
<title>Meeting Roulette</title>
|
||||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
<script type="importmap">
|
||||||
<script type="text/javascript" src="main.js"></script>
|
{
|
||||||
|
"imports": {
|
||||||
|
"vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link
|
<link
|
||||||
href="https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.6/daisyui.css"
|
href="https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.6/daisyui.css"
|
||||||
@@ -123,12 +129,8 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="hidden lg:block p-3 bg-base-200 rounded-box">
|
||||||
class="hidden lg:block p-3 bg-base-200 rounded-box"
|
<div class="stats stats-vertical w-full">
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="stats stats-vertical w-full"
|
|
||||||
>
|
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="stat-title">Meeting duration so far</div>
|
<div class="stat-title">Meeting duration so far</div>
|
||||||
<div class="stat-value" :id="rid + 1">
|
<div class="stat-value" :id="rid + 1">
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { createApp } from "vue";
|
||||||
|
|
||||||
const DAISYUI_THEMES = [
|
const DAISYUI_THEMES = [
|
||||||
"light",
|
"light",
|
||||||
"dark",
|
"dark",
|
||||||
@@ -32,7 +34,7 @@ const DAISYUI_THEMES = [
|
|||||||
"abyss",
|
"abyss",
|
||||||
];
|
];
|
||||||
|
|
||||||
let app = {
|
const app = createApp({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
rawData:
|
rawData:
|
||||||
@@ -51,18 +53,13 @@ let app = {
|
|||||||
initialSpin: true,
|
initialSpin: true,
|
||||||
initialColor: Math.floor(Math.random() * 4),
|
initialColor: Math.floor(Math.random() * 4),
|
||||||
rid: 0,
|
rid: 0,
|
||||||
beepTimer: undefined,
|
beepTimer: null,
|
||||||
sound: undefined,
|
sound: null,
|
||||||
themes: DAISYUI_THEMES,
|
themes: DAISYUI_THEMES,
|
||||||
currentTheme: "cmyk",
|
currentTheme: "cmyk",
|
||||||
spinning: false,
|
spinning: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
rawData() {
|
|
||||||
this.data = this.getData();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
selectedData() {
|
selectedData() {
|
||||||
return (
|
return (
|
||||||
@@ -83,12 +80,10 @@ let app = {
|
|||||||
return this.elapsedTime + this.totalRemainingTime;
|
return this.elapsedTime + this.totalRemainingTime;
|
||||||
},
|
},
|
||||||
totalExpectedTime() {
|
totalExpectedTime() {
|
||||||
return this.data.map((item) => item.time).reduce((a, b) => a + b, 0);
|
return this.data.reduce((sum, item) => sum + item.time, 0);
|
||||||
},
|
},
|
||||||
totalRemainingTime() {
|
totalRemainingTime() {
|
||||||
return this.filteredData
|
return this.filteredData.reduce((sum, item) => sum + item.time, 0);
|
||||||
.map((item) => item.time)
|
|
||||||
.reduce((a, b) => a + b, 0);
|
|
||||||
},
|
},
|
||||||
overtimeTime() {
|
overtimeTime() {
|
||||||
return this.totalTime - this.totalExpectedTime;
|
return this.totalTime - this.totalExpectedTime;
|
||||||
@@ -116,7 +111,7 @@ let app = {
|
|||||||
const textScale = this.textScale(item.text, angleRad);
|
const textScale = this.textScale(item.text, angleRad);
|
||||||
totalAngle += angleDeg;
|
totalAngle += angleDeg;
|
||||||
const colorIndex =
|
const colorIndex =
|
||||||
((index == this.filteredData.length - 1 && index % 4 == 0 ? 1 : 0) +
|
((index === this.filteredData.length - 1 && index % 4 === 0 ? 1 : 0) +
|
||||||
index +
|
index +
|
||||||
this.initialColor) %
|
this.initialColor) %
|
||||||
4;
|
4;
|
||||||
@@ -140,31 +135,56 @@ let app = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
rawData() {
|
||||||
|
this.data = this.getData();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.sound = new Audio("./sound.wav");
|
||||||
|
this.rawData = atob(this.getCookie("rawData", btoa(this.rawData)));
|
||||||
|
this.currentTheme = this.getCookie("theme", this.currentTheme);
|
||||||
|
this.data = this.getData();
|
||||||
|
setTimeout(this.showApp);
|
||||||
|
setInterval(() => {
|
||||||
|
this.rid = Math.random();
|
||||||
|
if (this.timerStarted) {
|
||||||
|
document.title = `${this.timerParts(0)}${this.timerParts(
|
||||||
|
1
|
||||||
|
)}:${this.timerParts(2)}:${this.timerParts(3)}`;
|
||||||
|
}
|
||||||
|
this.elapsedTime = (new Date() - this.meetingStart) / (1000 * 60);
|
||||||
|
this.date = new Date();
|
||||||
|
}, 200);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
textScale(text, angleRad) {
|
textScale(text, angleRad) {
|
||||||
const r = 1.2;
|
const ratio = 1.2;
|
||||||
const n = text.length;
|
return (
|
||||||
const k = n + r / (2 * Math.tan(Math.min(Math.PI, angleRad) / 2));
|
(text.length +
|
||||||
return k / r;
|
ratio / (2 * Math.tan(Math.min(Math.PI, angleRad) / 2))) /
|
||||||
|
ratio
|
||||||
|
);
|
||||||
},
|
},
|
||||||
overtime() {
|
overtime() {
|
||||||
return this.timerStarted && this.timerEnd - new Date() <= 0;
|
return this.timerStarted && this.timerEnd - new Date() <= 0;
|
||||||
},
|
},
|
||||||
timerParts(i) {
|
timerParts(index) {
|
||||||
const delta = this.timerStarted
|
let delta = 0;
|
||||||
? Math.floor((this.timerEnd - new Date()) / 1000)
|
if (this.timerStarted) {
|
||||||
: this.showSelected
|
delta = Math.floor((this.timerEnd - new Date()) / 1000);
|
||||||
? this.selectedData.time * 60
|
} else if (this.showSelected) {
|
||||||
: 0;
|
delta = this.selectedData.time * 60;
|
||||||
if (i == 0) {
|
}
|
||||||
|
if (index === 0) {
|
||||||
return delta < 0 ? "-" : "";
|
return delta < 0 ? "-" : "";
|
||||||
}
|
}
|
||||||
const hours = Math.floor(Math.abs(delta) / 3600);
|
const hours = Math.floor(Math.abs(delta) / 3600);
|
||||||
if (i == 1) {
|
if (index === 1) {
|
||||||
return String(hours).padStart(2, "0");
|
return String(hours).padStart(2, "0");
|
||||||
}
|
}
|
||||||
const minutes = Math.floor(Math.abs(delta) / 60 - hours * 60);
|
const minutes = Math.floor(Math.abs(delta) / 60 - hours * 60);
|
||||||
if (i == 2) {
|
if (index === 2) {
|
||||||
return String(minutes).padStart(2, "0");
|
return String(minutes).padStart(2, "0");
|
||||||
}
|
}
|
||||||
const seconds = Math.abs(delta) % 60;
|
const seconds = Math.abs(delta) % 60;
|
||||||
@@ -174,17 +194,16 @@ let app = {
|
|||||||
this.sound.play();
|
this.sound.play();
|
||||||
},
|
},
|
||||||
timeText(minutes, padHours = 0) {
|
timeText(minutes, padHours = 0) {
|
||||||
const prefix = minutes >= 0 ? '' : '-';
|
const prefix = minutes >= 0 ? "" : "-";
|
||||||
minutes = Math.abs(minutes);
|
const absMinutes = Math.abs(minutes);
|
||||||
if (minutes >= 60 || padHours > 0) {
|
if (absMinutes >= 60 || padHours > 0) {
|
||||||
return `${prefix}${Math.floor(minutes / 60)
|
return `${prefix}${Math.floor(absMinutes / 60)
|
||||||
.toFixed(0)
|
.toFixed(0)
|
||||||
.padStart(padHours, "0")}h${(minutes % 60)
|
.padStart(padHours, "0")}h${(absMinutes % 60)
|
||||||
.toFixed(0)
|
.toFixed(0)
|
||||||
.padStart(2, "0")}`;
|
.padStart(2, "0")}`;
|
||||||
} else {
|
|
||||||
return `${prefix}${(minutes % 60).toFixed(0).padStart(2, "0")}min`;
|
|
||||||
}
|
}
|
||||||
|
return `${prefix}${(absMinutes % 60).toFixed(0).padStart(2, "0")}min`;
|
||||||
},
|
},
|
||||||
spin() {
|
spin() {
|
||||||
if (this.timerStarted || this.noData) return;
|
if (this.timerStarted || this.noData) return;
|
||||||
@@ -204,7 +223,7 @@ let app = {
|
|||||||
particleCount: 400,
|
particleCount: 400,
|
||||||
startVelocity: 100,
|
startVelocity: 100,
|
||||||
spread: 100,
|
spread: 100,
|
||||||
origin: { y: 0.9 },
|
origin: { y: 0.9 }, // eslint-disable-line id-length
|
||||||
});
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
},
|
},
|
||||||
@@ -213,7 +232,7 @@ let app = {
|
|||||||
return this.svgData[0].id;
|
return this.svgData[0].id;
|
||||||
}
|
}
|
||||||
const angle = 360 - (this.wheelPosition % 360);
|
const angle = 360 - (this.wheelPosition % 360);
|
||||||
for (let index = 0; index < this.svgData.length; index++) {
|
for (let index = 0; index < this.svgData.length; index += 1) {
|
||||||
const element = this.svgData[index];
|
const element = this.svgData[index];
|
||||||
if (angle >= element.from && angle < element.to) {
|
if (angle >= element.from && angle < element.to) {
|
||||||
return element.id;
|
return element.id;
|
||||||
@@ -222,7 +241,8 @@ let app = {
|
|||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
getData() {
|
getData() {
|
||||||
const re = /:\s?(?:(?:(\d+)\s?h)?(\d+)?(?:\s?m(?:in)?)?)\s?$/i;
|
const re =
|
||||||
|
/:\s?(?:(?:(?<hours>\d+)\s?h)?(?<minutes>\d+)?(?:\s?m(?:in)?)?)\s?$/iu;
|
||||||
this.setCookie("rawData", btoa(this.rawData));
|
this.setCookie("rawData", btoa(this.rawData));
|
||||||
const data = this.rawData
|
const data = this.rawData
|
||||||
.split("\n")
|
.split("\n")
|
||||||
@@ -237,14 +257,15 @@ let app = {
|
|||||||
time: 1,
|
time: 1,
|
||||||
disabled: line.substring(0, 1) === "-",
|
disabled: line.substring(0, 1) === "-",
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
return {
|
return {
|
||||||
id: index,
|
id: index,
|
||||||
text: line.substring(0, line.indexOf(result[0])),
|
text: line.substring(0, result.index),
|
||||||
time: parseInt(result[1] ?? 0) * 60 + parseInt(result[2] ?? 0),
|
time:
|
||||||
|
parseInt(result.groups.hours ?? 0, 10) * 60 +
|
||||||
|
parseInt(result.groups.minutes ?? 0, 10),
|
||||||
disabled: line.substring(0, 1) === "-",
|
disabled: line.substring(0, 1) === "-",
|
||||||
};
|
};
|
||||||
}
|
|
||||||
});
|
});
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return [
|
return [
|
||||||
@@ -262,17 +283,18 @@ let app = {
|
|||||||
document.getElementById("app").setAttribute("style", "");
|
document.getElementById("app").setAttribute("style", "");
|
||||||
},
|
},
|
||||||
removeTopic() {
|
removeTopic() {
|
||||||
let i = 0;
|
let index = 0;
|
||||||
this.rawData = this.rawData
|
this.rawData = this.rawData
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map((line) => {
|
.map((line) => {
|
||||||
|
let newLine = line;
|
||||||
if (line.trim().length) {
|
if (line.trim().length) {
|
||||||
if (i === this.selected) {
|
if (index === this.selected) {
|
||||||
line = "-" + line;
|
newLine = `-${line}`;
|
||||||
}
|
}
|
||||||
i += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
return line;
|
return newLine;
|
||||||
})
|
})
|
||||||
.join("\n");
|
.join("\n");
|
||||||
this.showSelected = false;
|
this.showSelected = false;
|
||||||
@@ -292,23 +314,22 @@ let app = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setCookie(cname, cvalue, exdays) {
|
setCookie(cname, cvalue, exdays) {
|
||||||
const d = new Date();
|
const date = new Date();
|
||||||
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
date.setTime(date.getTime() + exdays * 24 * 60 * 60 * 1000);
|
||||||
let expires = "expires=" + d.toUTCString();
|
const expires = `expires=${date.toUTCString()}`;
|
||||||
console.log(cname + "=" + cvalue + "; path=/; " + expires);
|
document.cookie = `${cname}=${cvalue}; path=/; ${expires}`;
|
||||||
document.cookie = cname + "=" + cvalue + "; path=/; " + expires;
|
|
||||||
},
|
},
|
||||||
getCookie(cname, defaultValue) {
|
getCookie(cname, defaultValue) {
|
||||||
let name = cname + "=";
|
const name = `${cname}=`;
|
||||||
let decodedCookie = decodeURIComponent(document.cookie);
|
const decodedCookie = decodeURIComponent(document.cookie);
|
||||||
let ca = decodedCookie.split(";");
|
const ca = decodedCookie.split(";");
|
||||||
for (let i = 0; i < ca.length; i++) {
|
for (let index = 0; index < ca.length; index += 1) {
|
||||||
let c = ca[i];
|
let cookie = ca[index];
|
||||||
while (c.charAt(0) == " ") {
|
while (cookie.charAt(0) === " ") {
|
||||||
c = c.substring(1);
|
cookie = cookie.substring(1);
|
||||||
}
|
}
|
||||||
if (c.indexOf(name) == 0) {
|
if (cookie.indexOf(name) === 0) {
|
||||||
return c.substring(name.length, c.length);
|
return cookie.substring(name.length, cookie.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
@@ -318,27 +339,8 @@ let app = {
|
|||||||
this.setCookie("theme", this.currentTheme);
|
this.setCookie("theme", this.currentTheme);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted: function () {
|
});
|
||||||
console.log("app mounted");
|
|
||||||
this.sound = new Audio("./sound.wav");
|
|
||||||
this.rawData = atob(this.getCookie("rawData", btoa(this.rawData)));
|
|
||||||
this.currentTheme = this.getCookie("theme", this.currentTheme);
|
|
||||||
this.data = this.getData();
|
|
||||||
setTimeout(this.showApp);
|
|
||||||
setInterval(() => {
|
|
||||||
this.rid = Math.random();
|
|
||||||
if (this.timerStarted) {
|
|
||||||
document.title = `${this.timerParts(0)}${this.timerParts(
|
|
||||||
1
|
|
||||||
)}:${this.timerParts(2)}:${this.timerParts(3)}`;
|
|
||||||
}
|
|
||||||
this.elapsedTime = (new Date() - this.meetingStart) / (1000 * 60);
|
|
||||||
this.date = new Date();
|
|
||||||
}, 200);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
app = Vue.createApp(app);
|
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
};
|
};
|
||||||
|
|||||||
Generated
+2052
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "meeting-roulette",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "🎡 Spin your meetings",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint",
|
||||||
|
"fix": "eslint --fix"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/Klemek/meeting-roulette.git"
|
||||||
|
},
|
||||||
|
"author": "klemek",
|
||||||
|
"license": "ISC",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/Klemek/meeting-roulette/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/Klemek/meeting-roulette#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.21.0",
|
||||||
|
"eslint": "^9.21.0",
|
||||||
|
"eslint-config-prettier": "^10.0.2",
|
||||||
|
"eslint-plugin-vue": "^9.32.0",
|
||||||
|
"globals": "^16.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user