feat: add Berlin Clock layout and styling with Orbitron font
This commit is contained in:
committed by
Loic Coenen (aider)
parent
abf8b13fc7
commit
3fed6dc168
21
src/App.css
21
src/App.css
@@ -182,3 +182,24 @@
|
|||||||
border-right-color: var(--border);
|
border-right-color: var(--border);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.berlin-clock {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seconds-lamp-container {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lamp-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 330px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|||||||
10
src/App.tsx
10
src/App.tsx
@@ -3,7 +3,15 @@ import "./App.css";
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="clock-container">
|
||||||
|
<h1>Berlin Clock</h1>
|
||||||
|
<p>
|
||||||
|
The Berlin Clock (also known as the Mengenlehreuhr) uses lamps to represent the time.
|
||||||
|
The top lamp blinks every second. The next row of four red lamps each represents five hours.
|
||||||
|
The following row of four red lamps each represents one hour.
|
||||||
|
Then the next row of eleven lamps shows five‑minute blocks (every third lamp is red to
|
||||||
|
mark quarter hours). The last row shows the remaining minutes one by one.
|
||||||
|
</p>
|
||||||
<TimeConverter />
|
<TimeConverter />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,23 +14,17 @@ function getBackgroundColor(state: LampState): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getBorderRadius(variant?: LampProps["variant"]): string {
|
function getBorderRadius(variant?: LampProps["variant"]): string {
|
||||||
switch (variant) {
|
if (variant === "alone") return "50%";
|
||||||
case "alone":
|
if (variant === "first") return "16px 0 0 16px";
|
||||||
return "50%";
|
if (variant === "last") return "0 16px 16px 0";
|
||||||
case "first":
|
return "0";
|
||||||
return "50% 0 0 50%";
|
|
||||||
case "last":
|
|
||||||
return "0 50% 50% 0";
|
|
||||||
default:
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Lamp({ state, id, variant }: LampProps) {
|
export function Lamp({ state, id, variant }: LampProps) {
|
||||||
const style: React.CSSProperties = {
|
const style: React.CSSProperties = {
|
||||||
display: "inline-block",
|
display: "block",
|
||||||
width: 20,
|
width: variant === "alone" ? 50 : "100%",
|
||||||
height: 20,
|
height: 50,
|
||||||
backgroundColor: getBackgroundColor(state),
|
backgroundColor: getBackgroundColor(state),
|
||||||
borderRadius: getBorderRadius(variant),
|
borderRadius: getBorderRadius(variant),
|
||||||
margin: 1,
|
margin: 1,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function LampLine({ states, rowTestId }: LampLineProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid={rowTestId ?? "lamp-line"}
|
data-testid={rowTestId ?? "lamp-line"}
|
||||||
style={{ display: "flex", gap: 2, marginBottom: 5 }}
|
style={{ display: "flex", marginBottom: 10, width: "100%", justifyContent: "space-evenly" }}
|
||||||
>
|
>
|
||||||
{states.map((state, index) => {
|
{states.map((state, index) => {
|
||||||
let variant: "alone" | "first" | "last" | "middle" | undefined;
|
let variant: "alone" | "first" | "last" | "middle" | undefined;
|
||||||
@@ -25,12 +25,16 @@ export function LampLine({ states, rowTestId }: LampLineProps) {
|
|||||||
variant = "middle";
|
variant = "middle";
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Lamp
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
state={state}
|
style={{ flex: 1, display: "flex" }}
|
||||||
id={`lamp-${index}`}
|
>
|
||||||
variant={variant}
|
<Lamp
|
||||||
/>
|
state={state}
|
||||||
|
id={`lamp-${index}`}
|
||||||
|
variant={variant}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,17 +11,22 @@ export function BerlinClock({ enabled, clock }: BerlinClockProps) {
|
|||||||
if (!enabled) return null;
|
if (!enabled) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="berlin-clock" style={{ display: "flex", flexDirection: "column", alignItems: "center", width: "100%" }}>
|
||||||
<div
|
<div className="seconds-lamp-container" data-testid="seconds-row" style={{ width: "100%", display: "flex", justifyContent: "center" }}>
|
||||||
data-testid="seconds-row"
|
<Lamp state={clock.secondsRow[0]} variant="alone" id="seconds-lamp" />
|
||||||
style={{ display: "flex", gap: 2, marginBottom: 5 }}
|
</div>
|
||||||
>
|
<div className="lamp-row" style={{ width: "100%" }}>
|
||||||
<Lamp state={clock.secondsRow[0]} id="seconds-lamp" variant="alone" />
|
<LampLine states={clock.fiveHours} rowTestId="five-hours-row" />
|
||||||
|
</div>
|
||||||
|
<div className="lamp-row" style={{ width: "100%" }}>
|
||||||
|
<LampLine states={clock.oneHour} rowTestId="single-hours-row" />
|
||||||
|
</div>
|
||||||
|
<div className="lamp-row" style={{ width: "100%" }}>
|
||||||
|
<LampLine states={clock.fiveMinutes} rowTestId="five-minutes-row" />
|
||||||
|
</div>
|
||||||
|
<div className="lamp-row" style={{ width: "100%" }}>
|
||||||
|
<LampLine states={clock.oneMinute} rowTestId="single-minutes-row" />
|
||||||
</div>
|
</div>
|
||||||
<LampLine states={clock.fiveHours} rowTestId="five-hours-row" />
|
|
||||||
<LampLine states={clock.oneHour} rowTestId="single-hours-row" />
|
|
||||||
<LampLine states={clock.fiveMinutes} rowTestId="five-minutes-row" />
|
|
||||||
<LampLine states={clock.oneMinute} rowTestId="single-minutes-row" />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ export function TimeConverter() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<BerlinClock enabled={true} clock={berlinClock} />
|
||||||
|
<DigitalClock
|
||||||
|
currentTime={currentTime}
|
||||||
|
onTimeChange={setCurrentTime}
|
||||||
|
disabled={useSystemTime}
|
||||||
|
/>
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -33,12 +39,6 @@ export function TimeConverter() {
|
|||||||
/>
|
/>
|
||||||
Use system time
|
Use system time
|
||||||
</label>
|
</label>
|
||||||
<BerlinClock enabled={true} clock={berlinClock} />
|
|
||||||
<DigitalClock
|
|
||||||
currentTime={currentTime}
|
|
||||||
onTimeChange={setCurrentTime}
|
|
||||||
disabled={useSystemTime}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700&display=swap');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--text: #6b6375;
|
--text: #6b6375;
|
||||||
--text-h: #08060d;
|
--text-h: #08060d;
|
||||||
@@ -59,6 +61,8 @@
|
|||||||
min-height: 100svh;
|
min-height: 100svh;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,3 +113,11 @@ code {
|
|||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
background: var(--code-bg);
|
background: var(--code-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clock-container {
|
||||||
|
font-family: 'Orbitron', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-container input {
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user