DAISY UI
This commit is contained in:
+257
-89
@@ -7,6 +7,17 @@
|
|||||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||||
<script type="text/javascript" src="main.js"></script>
|
<script type="text/javascript" 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
|
||||||
|
href="https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.6/daisyui.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
||||||
|
<link
|
||||||
|
href="https://cdn.jsdelivr.net/npm/daisyui@5.0.0-beta.6/themes.css"
|
||||||
|
rel="stylesheet"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
<!-- card related -->
|
<!-- card related -->
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta property="og:title" content="Meeting Roulette" />
|
<meta property="og:title" content="Meeting Roulette" />
|
||||||
@@ -14,104 +25,261 @@
|
|||||||
<!-- <meta property="og:image" content="https://.../preview_640x320.jpg" /> -->
|
<!-- <meta property="og:image" content="https://.../preview_640x320.jpg" /> -->
|
||||||
<!-- <meta property="org:url" content="https://..." /> -->
|
<!-- <meta property="org:url" content="https://..." /> -->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="w-full h-screen max-h-screen">
|
||||||
<main id="app" style="display: none">
|
<main
|
||||||
<div class="left panel">
|
id="app"
|
||||||
<h2>Meeting Roulette</h2>
|
style="display: none"
|
||||||
<br />
|
class="flex flex-row h-screen w-full gap-6 p-6 max-h-screen"
|
||||||
<ul>
|
>
|
||||||
<li>
|
<div class="flex flex-col h-full gap-6 min-w-64">
|
||||||
Meeting started at: <b :id="rid + 1">{{ timeText(startedAt, 2) }}</b>
|
<div class="p-3 bg-base-200 rounded-box">
|
||||||
</li>
|
<div class="font-title font-black leading-none text-2xl">
|
||||||
<li>
|
Meeting Roulette
|
||||||
Meeting duration so far: <b :id="rid + 2">{{ timeText(elapsedTime) }}</b>
|
</div>
|
||||||
</li>
|
<p class="font-title font-light text-2xl">🎡 Spin your meetings</p>
|
||||||
<li>
|
</div>
|
||||||
Remaining meeting time: <b :id="rid + 3">{{ timeText(totalRemainingTime) }}</b>
|
<div
|
||||||
</li>
|
class="p-3 rounded-box text-center"
|
||||||
<li>
|
:class="overtime() ? 'text-error bg-error-content' : 'bg-base-200'"
|
||||||
End estimated at: <b :id="rid + 4">{{ timeText(estimatedEnd, 2) }}</b>
|
>
|
||||||
</li>
|
<span class="countdown font-mono text-5xl">
|
||||||
<li>
|
{{ timerParts(0) }}
|
||||||
Total meeting time: <b :id="rid + 5">{{ timeText(totalTime) }}</b>
|
<span :style="`--value:${timerParts(1)};`"
|
||||||
</li>
|
>{{ timerParts(1) }}</span
|
||||||
<li>
|
>
|
||||||
<label for="weighted">Weighted topics:</label> <input
|
:
|
||||||
id="weighted"
|
<span :style="`--value:${timerParts(2)};`"
|
||||||
v-model="weighted"
|
>{{ timerParts(2) }}</span
|
||||||
type="checkbox"
|
>
|
||||||
/>
|
:
|
||||||
</li>
|
<span :style="`--value: ${timerParts(3)};`"
|
||||||
</ul>
|
>{{ timerParts(3) }}</span
|
||||||
<br />
|
>
|
||||||
<div class="buttons">
|
</span>
|
||||||
<button :disabled="!showSelected" @click="timerFunction">
|
</div>
|
||||||
<span v-if="!timerStarted">Start timer</span>
|
<div class="flex flex-row gap-6">
|
||||||
<span v-else>Stop timer</span></button
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
|
class="btn btn-xl btn-success flex-1"
|
||||||
:disabled="!showSelected || timerStarted"
|
:disabled="!showSelected || timerStarted"
|
||||||
|
title="Start timer"
|
||||||
|
@click="timerFunction"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="size-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-xl btn-warning flex-1"
|
||||||
|
:disabled="!showSelected || !timerStarted"
|
||||||
|
title="Stop timer"
|
||||||
|
@click="timerFunction"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="size-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M5.25 7.5A2.25 2.25 0 0 1 7.5 5.25h9a2.25 2.25 0 0 1 2.25 2.25v9a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-9Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-xl btn-error flex-1"
|
||||||
|
:disabled="!showSelected || timerStarted"
|
||||||
|
title="Remove topic"
|
||||||
@click="removeTopic"
|
@click="removeTopic"
|
||||||
>
|
>
|
||||||
Remove topic
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="size-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h1 :id="rid">
|
<div class="p-3 bg-base-200 rounded-box stats stats-vertical">
|
||||||
<span :style="overtime() ? 'color: #B71C1C' : ''">
|
<div class="stat">
|
||||||
{{ timerText() }}
|
<div class="stat-title">Meeting duration so far</div>
|
||||||
</span>
|
<div class="stat-value" :id="rid + 1">
|
||||||
</h1>
|
{{ timeText(elapsedTime) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="middle panel">
|
<div class="stat-desc" :id="rid + 2">
|
||||||
<h1>Current topic: {{ showSelected ? selectedData.text : '???' }}</h1>
|
Started at: <b>{{ timeText(startedAt, 2) }}</b>
|
||||||
<h2 v-if="(showSelected || initialSpin) && !timerStarted">
|
</div>
|
||||||
click to spin
|
</div>
|
||||||
</h2>
|
<div class="stat">
|
||||||
<div class="wheel">
|
<div class="stat-title">Remaining meeting time</div>
|
||||||
<svg
|
<div class="stat-value" :id="rid + 3">
|
||||||
viewBox="-1.05 -1.05 2.1 2.1"
|
{{ timeText(totalRemainingTime) }}
|
||||||
:style="`transform: rotate(${wheelPosition}deg)`"
|
</div>
|
||||||
@click="spin"
|
<div class="stat-desc" :id="rid + 4">
|
||||||
>
|
End estimated at: <b>{{ timeText(estimatedEnd, 2) }}</b>
|
||||||
<circle
|
</div>
|
||||||
r="1"
|
</div>
|
||||||
cx="0"
|
<div class="stat">
|
||||||
cy="0"
|
<div class="stat-title">Total meeting time</div>
|
||||||
fill="#263238"
|
<div class="stat-value" :id="rid + 4">
|
||||||
stroke="#263238"
|
{{ timeText(totalTime) }}
|
||||||
stroke-width="0.04"
|
</div>
|
||||||
/>
|
<div class="stat-desc">
|
||||||
<g v-for="d in svgData" :transform="d.transform">
|
Overtime: <b>{{ timeText(overtimeTime) }}</b>
|
||||||
<path :d="d.path" :fill="d.color" />
|
</div>
|
||||||
<text
|
</div>
|
||||||
:x="d.textPosition"
|
</div>
|
||||||
y="0"
|
<div class="grow"></div>
|
||||||
style="
|
<div class="p-3 bg-base-200 rounded-box">
|
||||||
font: bold 1px sans-serif;
|
<div class="dropdown dropdown-top w-full">
|
||||||
text-align: center;
|
<div tabindex="0" role="button" class="btn m-1 w-full flex">
|
||||||
font-family: 'Courier New', Courier, monospace;
|
<div class="grow text-left">Theme</div>
|
||||||
"
|
<svg
|
||||||
dominant-baseline="middle"
|
width="12px"
|
||||||
text-anchor="end"
|
height="12px"
|
||||||
fill="white"
|
class="inline-block h-2 w-2 fill-current opacity-60"
|
||||||
:transform="d.textTransform"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 2048 2048"
|
||||||
>
|
>
|
||||||
{{ d.text }}
|
<path
|
||||||
</text>
|
d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z"
|
||||||
</g>
|
></path>
|
||||||
<circle
|
</svg>
|
||||||
r="10%"
|
</div>
|
||||||
cx="0"
|
<ul
|
||||||
cy="0"
|
tabindex="0"
|
||||||
fill="#cfd8dc"
|
class="dropdown-content max-h-100 overflow-scroll bg-base-300 rounded-box z-1 w-52 p-2 shadow-2xl"
|
||||||
stroke="#263238"
|
>
|
||||||
stroke-width="0.02"
|
<li v-for="theme in themes">
|
||||||
/>
|
<input
|
||||||
</svg>
|
type="radio"
|
||||||
|
name="theme-dropdown"
|
||||||
|
class="theme-controller btn btn-sm btn-block btn-ghost justify-start"
|
||||||
|
:aria-label="theme"
|
||||||
|
:value="theme"
|
||||||
|
:checked="theme === currentTheme"
|
||||||
|
@click="setTheme(theme)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right panel">
|
<div class="flex flex-col grow h-full gap-6">
|
||||||
<textarea v-model="rawData"></textarea>
|
<div
|
||||||
|
class="bg-base-200 rounded-box p-3 text-center font-black text-3xl"
|
||||||
|
>
|
||||||
|
Current topic: {{ showSelected ? selectedData.text : '???' }}
|
||||||
|
</div>
|
||||||
|
<div class="grow rounded-box p-3 overflow-hidden justify-center">
|
||||||
|
<div class="wheel relative h-full aspect-square m-auto">
|
||||||
|
<svg
|
||||||
|
viewBox="-1.05 -1.05 2.1 2.1"
|
||||||
|
class="h-full"
|
||||||
|
:class="timerStarted ? 'cursor-not-allowed' : 'cursor-pointer'"
|
||||||
|
:style="`transform: rotate(${wheelPosition}deg)`"
|
||||||
|
@click="spin"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
r="1"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
stroke-width="0.02"
|
||||||
|
style="stroke: var(--wheel); fill: var(--wheel)"
|
||||||
|
/>
|
||||||
|
<g v-for="d in svgData" :transform="d.transform">
|
||||||
|
<path :d="d.path" :style="d.backgroundStyle" />
|
||||||
|
<text
|
||||||
|
:x="d.textPosition"
|
||||||
|
y="0"
|
||||||
|
style="
|
||||||
|
font: bold 1px sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
"
|
||||||
|
dominant-baseline="middle"
|
||||||
|
text-anchor="end"
|
||||||
|
:style="d.textStyle"
|
||||||
|
:transform="d.textTransform"
|
||||||
|
>
|
||||||
|
{{ d.text }}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<circle
|
||||||
|
r="10%"
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
style="stroke: var(--wheel); fill: var(--color-base-100)"
|
||||||
|
stroke-width="0.01"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-base-200 rounded-box p-3 text-center">
|
||||||
|
<div v-if="timerStarted">Discuss the current topic</div>
|
||||||
|
<div v-else-if="initialSpin" class="animate-pulse">
|
||||||
|
Enter your <b>topics</b> then click the wheel to <b>spin</b> !
|
||||||
|
</div>
|
||||||
|
<div v-else-if="showSelected" class="animate-pulse">
|
||||||
|
Start <b>timer</b> or click the wheel to <b>spin</b>
|
||||||
|
</div>
|
||||||
|
<div v-else> </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col min-w-64 h-full gap-6">
|
||||||
|
<textarea
|
||||||
|
v-model="rawData"
|
||||||
|
class="grow bg-base-200 textarea rounded-box resize-none"
|
||||||
|
placeholder="Meeting point 1: 5min"
|
||||||
|
></textarea>
|
||||||
|
<div class="p-3 bg-base-200 rounded-box">
|
||||||
|
<fieldset class="fieldset">
|
||||||
|
<label class="fieldset-label">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="weighted"
|
||||||
|
v-model="weighted"
|
||||||
|
class="toggle"
|
||||||
|
/>
|
||||||
|
Weighted topics
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="bg-base-200 rounded-box p-3">
|
||||||
|
<a href="https://github.com/Klemek" target="_blank" class="link"
|
||||||
|
>@klemek</a
|
||||||
|
>
|
||||||
|
-
|
||||||
|
<a
|
||||||
|
href="https://github.com/Klemek/meeting-roulette"
|
||||||
|
target="_blank"
|
||||||
|
class="link"
|
||||||
|
>Github</a
|
||||||
|
>
|
||||||
|
- 2025
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,3 +1,37 @@
|
|||||||
|
const DAISYUI_THEMES = [
|
||||||
|
"light",
|
||||||
|
"dark",
|
||||||
|
"cupcake",
|
||||||
|
"bumblebee",
|
||||||
|
"emerald",
|
||||||
|
"corporate",
|
||||||
|
"synthwave",
|
||||||
|
"retro",
|
||||||
|
"cyberpunk",
|
||||||
|
"valentine",
|
||||||
|
"halloween",
|
||||||
|
"garden",
|
||||||
|
"forest",
|
||||||
|
"aqua",
|
||||||
|
"pastel",
|
||||||
|
"fantasy",
|
||||||
|
"luxury",
|
||||||
|
"dracula",
|
||||||
|
"cmyk",
|
||||||
|
"autumn",
|
||||||
|
"business",
|
||||||
|
"acid",
|
||||||
|
"lemonade",
|
||||||
|
"night",
|
||||||
|
"coffee",
|
||||||
|
"winter",
|
||||||
|
"dim",
|
||||||
|
"nord",
|
||||||
|
"sunset",
|
||||||
|
"caramellatte",
|
||||||
|
"abyss",
|
||||||
|
];
|
||||||
|
|
||||||
let app = {
|
let app = {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -15,10 +49,12 @@ let app = {
|
|||||||
meetingStart: new Date(),
|
meetingStart: new Date(),
|
||||||
elapsedTime: 0,
|
elapsedTime: 0,
|
||||||
initialSpin: true,
|
initialSpin: true,
|
||||||
initialColor: Math.random() * 360,
|
initialColor: Math.floor(Math.random() * 4),
|
||||||
rid: 0,
|
rid: 0,
|
||||||
beepTimer: undefined,
|
beepTimer: undefined,
|
||||||
sound: undefined,
|
sound: undefined,
|
||||||
|
themes: DAISYUI_THEMES,
|
||||||
|
currentTheme: "light",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -43,9 +79,7 @@ let app = {
|
|||||||
return this.elapsedTime + this.totalRemainingTime;
|
return this.elapsedTime + this.totalRemainingTime;
|
||||||
},
|
},
|
||||||
totalExpectedTime() {
|
totalExpectedTime() {
|
||||||
return this.data
|
return this.data.map((item) => item.time).reduce((a, b) => a + b, 0);
|
||||||
.map((item) => item.time)
|
|
||||||
.reduce((a, b) => a + b, 0);
|
|
||||||
},
|
},
|
||||||
totalRemainingTime() {
|
totalRemainingTime() {
|
||||||
return this.filteredData
|
return this.filteredData
|
||||||
@@ -62,7 +96,9 @@ let app = {
|
|||||||
estimatedEnd() {
|
estimatedEnd() {
|
||||||
const end = new Date(this.meetingStart.getTime());
|
const end = new Date(this.meetingStart.getTime());
|
||||||
const timerDelta = (this.timerEnd - this.date) / (1000 * 60);
|
const timerDelta = (this.timerEnd - this.date) / (1000 * 60);
|
||||||
end.setMinutes(end.getMinutes() + this.totalTime - (this.timerStarted ? timerDelta : 0));
|
end.setMinutes(
|
||||||
|
end.getMinutes() + this.totalTime - (this.timerStarted ? timerDelta : 0)
|
||||||
|
);
|
||||||
return end.getHours() * 60 + end.getMinutes();
|
return end.getHours() * 60 + end.getMinutes();
|
||||||
},
|
},
|
||||||
svgData() {
|
svgData() {
|
||||||
@@ -75,6 +111,12 @@ let app = {
|
|||||||
const angleDeg = (360 * ratio) % 360;
|
const angleDeg = (360 * ratio) % 360;
|
||||||
const textScale = this.textScale(item.text, angleRad);
|
const textScale = this.textScale(item.text, angleRad);
|
||||||
totalAngle += angleDeg;
|
totalAngle += angleDeg;
|
||||||
|
const colorIndex =
|
||||||
|
((index == this.filteredData.length - 1 && index % 4 == 0 ? 1 : 0) +
|
||||||
|
index +
|
||||||
|
this.initialColor) %
|
||||||
|
4;
|
||||||
|
const color = ["primary", "secondary", "accent", "neutral"][colorIndex];
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
text: item.text,
|
text: item.text,
|
||||||
@@ -86,7 +128,8 @@ let app = {
|
|||||||
angleRad / 2
|
angleRad / 2
|
||||||
)} ${Math.sin(angleRad / 2)} z`,
|
)} ${Math.sin(angleRad / 2)} z`,
|
||||||
transform: `rotate(${totalAngle - angleDeg / 2}, 0, 0)`,
|
transform: `rotate(${totalAngle - angleDeg / 2}, 0, 0)`,
|
||||||
color: `hsl(${this.initialColor + 100 * index} 80% 50%)`,
|
textStyle: `fill: var(--color-${color}-content)`,
|
||||||
|
backgroundStyle: `fill: var(--color-${color})`,
|
||||||
from: totalAngle - angleDeg,
|
from: totalAngle - angleDeg,
|
||||||
to: totalAngle,
|
to: totalAngle,
|
||||||
};
|
};
|
||||||
@@ -103,22 +146,36 @@ let app = {
|
|||||||
overtime() {
|
overtime() {
|
||||||
return this.timerStarted && this.timerEnd - new Date() <= 0;
|
return this.timerStarted && this.timerEnd - new Date() <= 0;
|
||||||
},
|
},
|
||||||
timerText() {
|
timerParts(i) {
|
||||||
const delta = this.timerStarted
|
const delta = this.timerStarted
|
||||||
? Math.floor((this.timerEnd - new Date()) / 1000)
|
? Math.floor((this.timerEnd - new Date()) / 1000)
|
||||||
: (this.showSelected
|
: this.showSelected
|
||||||
? this.selectedData.time * 60
|
? this.selectedData.time * 60
|
||||||
: 0);
|
: 0;
|
||||||
return `${delta < 0 ? "-" : ""}${String(
|
if (i == 0) {
|
||||||
Math.floor(Math.abs(delta) / 60)
|
return delta < 0 ? "-" : "";
|
||||||
).padStart(2, "0")}:${String(Math.abs(delta) % 60).padStart(2, "0")}`;
|
}
|
||||||
|
const hours = Math.floor(Math.abs(delta) / 3600);
|
||||||
|
if (i == 1) {
|
||||||
|
return String(hours).padStart(2, "0");
|
||||||
|
}
|
||||||
|
const minutes = Math.floor(Math.abs(delta) / 60 - hours * 60);
|
||||||
|
if (i == 2) {
|
||||||
|
return String(minutes).padStart(2, "0");
|
||||||
|
}
|
||||||
|
const seconds = Math.abs(delta) % 60;
|
||||||
|
return String(seconds).padStart(2, "0");
|
||||||
},
|
},
|
||||||
beep() {
|
beep() {
|
||||||
this.sound.play();
|
this.sound.play();
|
||||||
},
|
},
|
||||||
timeText(minutes, padHours = 0) {
|
timeText(minutes, padHours = 0) {
|
||||||
if (minutes >= 60 || padHours > 0) {
|
if (minutes >= 60 || padHours > 0) {
|
||||||
return `${Math.floor(minutes / 60).toFixed(0).padStart(padHours, "0")}h${(minutes % 60).toFixed(0).padStart(2, "0")}`;
|
return `${Math.floor(minutes / 60)
|
||||||
|
.toFixed(0)
|
||||||
|
.padStart(padHours, "0")}h${(minutes % 60)
|
||||||
|
.toFixed(0)
|
||||||
|
.padStart(2, "0")}`;
|
||||||
} else {
|
} else {
|
||||||
return `${(minutes % 60).toFixed(0).padStart(2, "0")}min`;
|
return `${(minutes % 60).toFixed(0).padStart(2, "0")}min`;
|
||||||
}
|
}
|
||||||
@@ -173,12 +230,14 @@ let app = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
return [{
|
return [
|
||||||
id: 0,
|
{
|
||||||
text: '?',
|
id: 0,
|
||||||
time: 1,
|
text: "?",
|
||||||
disabled: false,
|
time: 1,
|
||||||
}];
|
disabled: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
@@ -217,7 +276,7 @@ let app = {
|
|||||||
},
|
},
|
||||||
setCookie(cname, cvalue, exdays) {
|
setCookie(cname, cvalue, exdays) {
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
|
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
||||||
let expires = "expires=" + d.toUTCString();
|
let expires = "expires=" + d.toUTCString();
|
||||||
console.log(cname + "=" + cvalue + "; path=/; " + expires);
|
console.log(cname + "=" + cvalue + "; path=/; " + expires);
|
||||||
document.cookie = cname + "=" + cvalue + "; path=/; " + expires;
|
document.cookie = cname + "=" + cvalue + "; path=/; " + expires;
|
||||||
@@ -225,7 +284,7 @@ let app = {
|
|||||||
getCookie(cname, defaultValue) {
|
getCookie(cname, defaultValue) {
|
||||||
let name = cname + "=";
|
let name = cname + "=";
|
||||||
let decodedCookie = decodeURIComponent(document.cookie);
|
let decodedCookie = decodeURIComponent(document.cookie);
|
||||||
let ca = decodedCookie.split(';');
|
let ca = decodedCookie.split(";");
|
||||||
for (let i = 0; i < ca.length; i++) {
|
for (let i = 0; i < ca.length; i++) {
|
||||||
let c = ca[i];
|
let c = ca[i];
|
||||||
while (c.charAt(0) == " ") {
|
while (c.charAt(0) == " ") {
|
||||||
@@ -237,17 +296,24 @@ let app = {
|
|||||||
}
|
}
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
},
|
},
|
||||||
|
setTheme(value) {
|
||||||
|
this.currentTheme = value;
|
||||||
|
this.setCookie("theme", this.currentTheme);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
console.log("app mounted");
|
console.log("app mounted");
|
||||||
this.sound = new Audio("./sound.wav");
|
this.sound = new Audio("./sound.wav");
|
||||||
this.rawData = atob(this.getCookie("rawData", btoa(this.rawData)));
|
this.rawData = atob(this.getCookie("rawData", btoa(this.rawData)));
|
||||||
|
this.currentTheme = this.getCookie("theme", "light");
|
||||||
this.data = this.getData();
|
this.data = this.getData();
|
||||||
setTimeout(this.showApp);
|
setTimeout(this.showApp);
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.rid = Math.random();
|
this.rid = Math.random();
|
||||||
if (this.timerStarted) {
|
if (this.timerStarted) {
|
||||||
document.title = this.timerText();
|
document.title = `${this.timerParts(0)}${this.timerParts(
|
||||||
|
1
|
||||||
|
)}:${this.timerParts(2)}:${this.timerParts(3)}`;
|
||||||
}
|
}
|
||||||
this.elapsedTime = (new Date() - this.meetingStart) / (1000 * 60);
|
this.elapsedTime = (new Date() - this.meetingStart) / (1000 * 60);
|
||||||
this.date = new Date();
|
this.date = new Date();
|
||||||
|
|||||||
@@ -1,141 +1,13 @@
|
|||||||
/*
|
:root {
|
||||||
=================================================
|
--wheel: color-mix(
|
||||||
https://www.joshwcomeau.com/css/custom-css-reset/
|
in oklch,
|
||||||
=================================================
|
var(--color-base-content) 30%,
|
||||||
*/
|
var(--color-base-100)
|
||||||
|
);
|
||||||
/*
|
|
||||||
1. Use a more-intuitive box-sizing model.
|
|
||||||
*/
|
|
||||||
*,
|
|
||||||
*::before,
|
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
2. Remove default margin
|
|
||||||
*/
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
3. Allow percentage-based heights in the application
|
|
||||||
*/
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Typographic tweaks!
|
|
||||||
4. Add accessible line-height
|
|
||||||
5. Improve text rendering
|
|
||||||
*/
|
|
||||||
body {
|
|
||||||
line-height: 1.5;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
6. Improve media defaults
|
|
||||||
*/
|
|
||||||
img,
|
|
||||||
picture,
|
|
||||||
video,
|
|
||||||
canvas,
|
|
||||||
svg {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
7. Remove built-in form typography styles
|
|
||||||
*/
|
|
||||||
input,
|
|
||||||
button,
|
|
||||||
textarea,
|
|
||||||
select {
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
8. Avoid text overflows
|
|
||||||
*/
|
|
||||||
p,
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
9. Create a root stacking context
|
|
||||||
*/
|
|
||||||
#root,
|
|
||||||
#__next {
|
|
||||||
isolation: isolate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
=================================================
|
|
||||||
CUSTOM STYLE
|
|
||||||
=================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#app {
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: stretch;
|
|
||||||
background-color: #eceff1;
|
|
||||||
color: #263238;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
flex-grow: 2;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel.right,
|
|
||||||
.panel.left {
|
|
||||||
background-color: #cfd8dc;
|
|
||||||
margin: 1em;
|
|
||||||
border-radius: 2em;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel.middle {
|
|
||||||
height: 100%;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel.middle h1 {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel.middle h2 {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wheel {
|
|
||||||
position: relative;
|
|
||||||
height: 80%;
|
|
||||||
width: fit-content;
|
|
||||||
margin: 10% auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wheel svg {
|
.wheel svg {
|
||||||
transition: transform 5s ease-out;
|
transition: transform 5s ease-out;
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svg text {
|
svg text {
|
||||||
@@ -153,20 +25,10 @@ svg text {
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-right: 6vh solid #263238;
|
border-right: 6vh solid
|
||||||
|
color-mix(in oklch, var(--color-base-content) 40%, var(--color-base-100));
|
||||||
border-bottom: 2vh solid transparent;
|
border-bottom: 2vh solid transparent;
|
||||||
border-top: 2vh solid transparent;
|
border-top: 2vh solid transparent;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel.right textarea {
|
|
||||||
background-color: #cfd8dc;
|
|
||||||
color: #263238;
|
|
||||||
font-family: sans-serif;
|
|
||||||
padding: 0.5em;
|
|
||||||
border: 1px solid #263238;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 1em;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user