Compare commits
4 Commits
9726cff90c
...
3be055608e
| Author | SHA1 | Date | |
|---|---|---|---|
| 3be055608e | |||
| 651a2d8dc2 | |||
| 9dae4a363e | |||
| a4979186a5 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -22,3 +22,5 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
dist.tar.gz
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
source ~/.nvm/nvm.sh
|
source ~/.nvm/nvm.sh
|
||||||
nvm use
|
nvm use
|
||||||
|
npm run build
|
||||||
|
tar zcvf dist.tar.gz dist
|
||||||
|
|||||||
147
src/App.css
147
src/App.css
@ -13,6 +13,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding-top: 40px;
|
padding-top: 40px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style selector */
|
/* Style selector */
|
||||||
@ -133,6 +134,23 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.digital-clock-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.digital-clock-timezone {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.style-button {
|
.style-button {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -166,3 +184,132 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Settings button */
|
||||||
|
.settings-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 20px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings overlay */
|
||||||
|
.settings-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings dialog */
|
||||||
|
.settings-dialog {
|
||||||
|
background-color: var(--bg);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 400px;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
color: var(--text-h);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-close {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--text);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-close:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-label {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-toggle {
|
||||||
|
width: 44px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 2px;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-toggle.active {
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-slider {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-toggle.active .toggle-slider {
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
|||||||
155
src/App.tsx
155
src/App.tsx
@ -23,10 +23,19 @@ function formatDate(date: Date): DateInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo }: { time: string; size: number; period?: string; is24Hour: boolean; onToggleFormat: () => void; dateInfo: DateInfo }) {
|
function getTimezoneInfo(): string {
|
||||||
|
// 获取本地时区名称,如 "Asia/Shanghai"
|
||||||
|
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
|
// 获取时区偏移,如 "GMT+8"
|
||||||
|
const offset = new Date().toLocaleTimeString('en-US', { timeZoneName: 'short' }).split(' ').pop() || ''
|
||||||
|
return `${timeZone} ${offset}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo, timeZone, showDate, showTimezone }: { time: string; size: number; period?: string; is24Hour: boolean; onToggleFormat: () => void; dateInfo: DateInfo; timeZone: string; showDate: boolean; showTimezone: boolean }) {
|
||||||
const fontSize = Math.floor(size * 0.95)
|
const fontSize = Math.floor(size * 0.95)
|
||||||
const periodSize = Math.floor(size * 0.20)
|
const periodSize = Math.floor(size * 0.20)
|
||||||
const dateSize = Math.floor(size * 0.20)
|
const dateSize = Math.floor(size * 0.20)
|
||||||
|
const timeZoneSize = Math.floor(size * 0.20)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="digital-clock-outer">
|
<div className="digital-clock-outer">
|
||||||
@ -50,12 +59,14 @@ function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo }
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{showDate && (
|
||||||
<span
|
<span
|
||||||
className="digital-clock-date"
|
className="digital-clock-date"
|
||||||
style={{ fontSize: `${dateSize}px` }}
|
style={{ fontSize: `${dateSize}px` }}
|
||||||
>
|
>
|
||||||
{dateInfo.dateString} {dateInfo.weekday}
|
{dateInfo.dateString} {dateInfo.weekday}
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="digital-clock"
|
className="digital-clock"
|
||||||
@ -63,6 +74,16 @@ function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo }
|
|||||||
>
|
>
|
||||||
{time}
|
{time}
|
||||||
</div>
|
</div>
|
||||||
|
{showTimezone && (
|
||||||
|
<div className="digital-clock-footer">
|
||||||
|
<span
|
||||||
|
className="digital-clock-timezone"
|
||||||
|
style={{ fontSize: `${timeZoneSize}px` }}
|
||||||
|
>
|
||||||
|
{timeZone}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -70,9 +91,68 @@ function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo }
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [time, setTime] = useState(new Date())
|
const [time, setTime] = useState(new Date())
|
||||||
const [clockStyle, setClockStyle] = useState<ClockStyle>('digital')
|
const [clockStyle, setClockStyle] = useState<ClockStyle>(() => {
|
||||||
|
// 从 localStorage 读取,默认数码管 ('digital')
|
||||||
|
const saved = localStorage.getItem('clock-style')
|
||||||
|
const validStyles: ClockStyle[] = ['digital', 'flip', 'roll']
|
||||||
|
return saved && validStyles.includes(saved as ClockStyle) ? (saved as ClockStyle) : 'digital'
|
||||||
|
})
|
||||||
const [clockSize, setClockSize] = useState(120)
|
const [clockSize, setClockSize] = useState(120)
|
||||||
const [is24Hour, setIs24Hour] = useState(true)
|
const [is24Hour, setIs24Hour] = useState(() => {
|
||||||
|
// 从 localStorage 读取,默认 24 小时制 (true)
|
||||||
|
const saved = localStorage.getItem('clock-is24Hour')
|
||||||
|
return saved === null ? true : saved === 'true'
|
||||||
|
})
|
||||||
|
const [rollDirection, setRollDirection] = useState<'down' | 'up'>(() => {
|
||||||
|
// 从 localStorage 读取,默认向下滚动 ('down')
|
||||||
|
const saved = localStorage.getItem('clock-rollDirection')
|
||||||
|
return saved === 'up' ? 'up' : 'down'
|
||||||
|
})
|
||||||
|
const [showSettings, setShowSettings] = useState(false)
|
||||||
|
const [showDate, setShowDate] = useState(() => {
|
||||||
|
// 从 localStorage 读取,默认显示日期 (true)
|
||||||
|
const saved = localStorage.getItem('clock-showDate')
|
||||||
|
return saved === null ? true : saved === 'true'
|
||||||
|
})
|
||||||
|
const [showTimezone, setShowTimezone] = useState(() => {
|
||||||
|
// 从 localStorage 读取,默认显示时区 (true)
|
||||||
|
const saved = localStorage.getItem('clock-showTimezone')
|
||||||
|
return saved === null ? true : saved === 'true'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 切换样式时保存到 localStorage
|
||||||
|
const handleSetClockStyle = (style: ClockStyle) => {
|
||||||
|
setClockStyle(style)
|
||||||
|
localStorage.setItem('clock-style', style)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换格式时保存到 localStorage
|
||||||
|
const toggleFormat = () => {
|
||||||
|
const newValue = !is24Hour
|
||||||
|
setIs24Hour(newValue)
|
||||||
|
localStorage.setItem('clock-is24Hour', String(newValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换滚动方向时保存到 localStorage
|
||||||
|
const toggleRollDirection = () => {
|
||||||
|
const newValue = rollDirection === 'down' ? 'up' : 'down'
|
||||||
|
setRollDirection(newValue)
|
||||||
|
localStorage.setItem('clock-rollDirection', newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换显示日期时保存到 localStorage
|
||||||
|
const toggleShowDate = () => {
|
||||||
|
const newValue = !showDate
|
||||||
|
setShowDate(newValue)
|
||||||
|
localStorage.setItem('clock-showDate', String(newValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换显示时区时保存到 localStorage
|
||||||
|
const toggleShowTimezone = () => {
|
||||||
|
const newValue = !showTimezone
|
||||||
|
setShowTimezone(newValue)
|
||||||
|
localStorage.setItem('clock-showTimezone', String(newValue))
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
@ -118,13 +198,14 @@ function App() {
|
|||||||
|
|
||||||
const { timeString, period } = formatTime()
|
const { timeString, period } = formatTime()
|
||||||
const dateInfo = formatDate(time)
|
const dateInfo = formatDate(time)
|
||||||
|
const timeZone = getTimezoneInfo()
|
||||||
|
|
||||||
const renderClock = () => {
|
const renderClock = () => {
|
||||||
switch (clockStyle) {
|
switch (clockStyle) {
|
||||||
case 'flip':
|
case 'flip':
|
||||||
return <FlipClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} />
|
return <FlipClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} is24Hour={is24Hour} onToggleFormat={toggleFormat} timeZone={timeZone} showDate={showDate} showTimezone={showTimezone} />
|
||||||
case 'roll':
|
case 'roll':
|
||||||
return <RollClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} />
|
return <RollClock time={timeString} size={clockSize} dateInfo={dateInfo} period={period} is24Hour={is24Hour} onToggleFormat={toggleFormat} direction={rollDirection} onToggleDirection={toggleRollDirection} timeZone={timeZone} showDate={showDate} showTimezone={showTimezone} />
|
||||||
case 'digital':
|
case 'digital':
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
@ -133,8 +214,11 @@ function App() {
|
|||||||
size={clockSize}
|
size={clockSize}
|
||||||
period={period}
|
period={period}
|
||||||
is24Hour={is24Hour}
|
is24Hour={is24Hour}
|
||||||
onToggleFormat={() => setIs24Hour(!is24Hour)}
|
onToggleFormat={toggleFormat}
|
||||||
dateInfo={dateInfo}
|
dateInfo={dateInfo}
|
||||||
|
timeZone={timeZone}
|
||||||
|
showDate={showDate}
|
||||||
|
showTimezone={showTimezone}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -142,22 +226,75 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
|
<button
|
||||||
|
className="settings-button"
|
||||||
|
onClick={() => setShowSettings(true)}
|
||||||
|
aria-label="设置"
|
||||||
|
>
|
||||||
|
⚙️
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showSettings && (
|
||||||
|
<div className="settings-overlay" onClick={() => setShowSettings(false)}>
|
||||||
|
<div className="settings-dialog" onClick={(e) => e.stopPropagation()}>
|
||||||
|
<div className="settings-header">
|
||||||
|
<h3>设置</h3>
|
||||||
|
<button
|
||||||
|
className="settings-close"
|
||||||
|
onClick={() => setShowSettings(false)}
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="settings-content">
|
||||||
|
<div className="settings-row">
|
||||||
|
<span className="settings-label">12小时制</span>
|
||||||
|
<button
|
||||||
|
className={`settings-toggle ${!is24Hour ? 'active' : ''}`}
|
||||||
|
onClick={toggleFormat}
|
||||||
|
>
|
||||||
|
<span className="toggle-slider" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="settings-row">
|
||||||
|
<span className="settings-label">显示日期</span>
|
||||||
|
<button
|
||||||
|
className={`settings-toggle ${showDate ? 'active' : ''}`}
|
||||||
|
onClick={toggleShowDate}
|
||||||
|
>
|
||||||
|
<span className="toggle-slider" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="settings-row">
|
||||||
|
<span className="settings-label">显示时区</span>
|
||||||
|
<button
|
||||||
|
className={`settings-toggle ${showTimezone ? 'active' : ''}`}
|
||||||
|
onClick={toggleShowTimezone}
|
||||||
|
>
|
||||||
|
<span className="toggle-slider" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="style-selector">
|
<div className="style-selector">
|
||||||
<button
|
<button
|
||||||
className={`style-button ${clockStyle === 'digital' ? 'active' : ''}`}
|
className={`style-button ${clockStyle === 'digital' ? 'active' : ''}`}
|
||||||
onClick={() => setClockStyle('digital')}
|
onClick={() => handleSetClockStyle('digital')}
|
||||||
>
|
>
|
||||||
数码管
|
数码管
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`style-button ${clockStyle === 'flip' ? 'active' : ''}`}
|
className={`style-button ${clockStyle === 'flip' ? 'active' : ''}`}
|
||||||
onClick={() => setClockStyle('flip')}
|
onClick={() => handleSetClockStyle('flip')}
|
||||||
>
|
>
|
||||||
翻页
|
翻页
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={`style-button ${clockStyle === 'roll' ? 'active' : ''}`}
|
className={`style-button ${clockStyle === 'roll' ? 'active' : ''}`}
|
||||||
onClick={() => setClockStyle('roll')}
|
onClick={() => handleSetClockStyle('roll')}
|
||||||
>
|
>
|
||||||
滚动
|
滚动
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -83,6 +83,23 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flip-clock-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flip-clock-timezone {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.flip-clock-digit-group {
|
.flip-clock-digit-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { FlipDigit } from './FlipDigit';
|
import { FlipDigit } from './FlipDigit';
|
||||||
import './FlipClock.css';
|
import './FlipClock.css';
|
||||||
|
|
||||||
@ -12,24 +11,28 @@ interface FlipClockProps {
|
|||||||
size: number;
|
size: number;
|
||||||
dateInfo: DateInfo;
|
dateInfo: DateInfo;
|
||||||
period?: string;
|
period?: string;
|
||||||
|
is24Hour: boolean;
|
||||||
|
onToggleFormat: () => void;
|
||||||
|
timeZone: string;
|
||||||
|
showDate: boolean;
|
||||||
|
showTimezone: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FlipClock({ time, size, dateInfo, period }: FlipClockProps) {
|
export function FlipClock({ time, size, dateInfo, period, is24Hour, onToggleFormat, timeZone, showDate, showTimezone }: FlipClockProps) {
|
||||||
const [is24Hour, setIs24Hour] = useState(true);
|
|
||||||
|
|
||||||
// Parse time - it's already in 12h format when period is provided
|
// Parse time - it's already in 12h format when period is provided
|
||||||
const [hoursStr, minutes, seconds] = time.split(':');
|
const [hoursStr, minutes, seconds] = time.split(':');
|
||||||
const displayHours = hoursStr;
|
const displayHours = hoursStr;
|
||||||
|
|
||||||
const periodSize = size * 0.20;
|
const periodSize = size * 0.20;
|
||||||
const dateSize = size * 0.20;
|
const dateSize = size * 0.20;
|
||||||
|
const timeZoneSize = size * 0.20;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flip-clock-outer">
|
<div className="flip-clock-outer">
|
||||||
<div className="flip-clock-side-panel">
|
<div className="flip-clock-side-panel">
|
||||||
<button
|
<button
|
||||||
className="flip-format-toggle"
|
className="flip-format-toggle"
|
||||||
onClick={() => setIs24Hour(!is24Hour)}
|
onClick={onToggleFormat}
|
||||||
>
|
>
|
||||||
{is24Hour ? '24H' : '12H'}
|
{is24Hour ? '24H' : '12H'}
|
||||||
</button>
|
</button>
|
||||||
@ -46,12 +49,14 @@ export function FlipClock({ time, size, dateInfo, period }: FlipClockProps) {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{showDate && (
|
||||||
<span
|
<span
|
||||||
className="flip-clock-date"
|
className="flip-clock-date"
|
||||||
style={{ fontSize: `${dateSize}px` }}
|
style={{ fontSize: `${dateSize}px` }}
|
||||||
>
|
>
|
||||||
{dateInfo.dateString} {dateInfo.weekday}
|
{dateInfo.dateString} {dateInfo.weekday}
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flip-clock-container">
|
<div className="flip-clock-container">
|
||||||
{/* Hours */}
|
{/* Hours */}
|
||||||
@ -92,6 +97,16 @@ export function FlipClock({ time, size, dateInfo, period }: FlipClockProps) {
|
|||||||
<FlipDigit value={parseInt(seconds[1], 10)} size={size} />
|
<FlipDigit value={parseInt(seconds[1], 10)} size={size} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{showTimezone && (
|
||||||
|
<div className="flip-clock-footer">
|
||||||
|
<span
|
||||||
|
className="flip-clock-timezone"
|
||||||
|
style={{ fontSize: `${timeZoneSize}px` }}
|
||||||
|
>
|
||||||
|
{timeZone}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -89,6 +89,23 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.roll-clock-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roll-clock-timezone {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.roll-clock-digit-group {
|
.roll-clock-digit-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { RollDigit } from './RollDigit';
|
import { RollDigit } from './RollDigit';
|
||||||
import './RollClock.css';
|
import './RollClock.css';
|
||||||
|
|
||||||
@ -12,13 +11,16 @@ interface RollClockProps {
|
|||||||
size: number;
|
size: number;
|
||||||
dateInfo: DateInfo;
|
dateInfo: DateInfo;
|
||||||
period?: string;
|
period?: string;
|
||||||
|
is24Hour: boolean;
|
||||||
|
onToggleFormat: () => void;
|
||||||
|
direction: 'down' | 'up';
|
||||||
|
onToggleDirection: () => void;
|
||||||
|
timeZone: string;
|
||||||
|
showDate: boolean;
|
||||||
|
showTimezone: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RollClock({ time, size, dateInfo, period }: RollClockProps) {
|
export function RollClock({ time, size, dateInfo, period, is24Hour, onToggleFormat, direction, onToggleDirection, timeZone, showDate, showTimezone }: RollClockProps) {
|
||||||
// Roll direction: 'down' = roll down, 'up' = roll up
|
|
||||||
const [direction, setDirection] = useState<'down' | 'up'>('down');
|
|
||||||
const [is24Hour, setIs24Hour] = useState(true);
|
|
||||||
|
|
||||||
// Parse time - it's already in 12h format when period is provided
|
// Parse time - it's already in 12h format when period is provided
|
||||||
const [hoursStr, minutes, seconds] = time.split(':');
|
const [hoursStr, minutes, seconds] = time.split(':');
|
||||||
const displayHours = hoursStr;
|
const displayHours = hoursStr;
|
||||||
@ -26,18 +28,14 @@ export function RollClock({ time, size, dateInfo, period }: RollClockProps) {
|
|||||||
const buttonSize = size * 0.25;
|
const buttonSize = size * 0.25;
|
||||||
const periodSize = size * 0.20;
|
const periodSize = size * 0.20;
|
||||||
const dateSize = size * 0.20;
|
const dateSize = size * 0.20;
|
||||||
|
const timeZoneSize = size * 0.20;
|
||||||
// Toggle direction
|
|
||||||
const toggleDirection = () => {
|
|
||||||
setDirection((prev) => (prev === 'down' ? 'up' : 'down'));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="roll-clock-outer">
|
<div className="roll-clock-outer">
|
||||||
<div className="roll-clock-side-panel">
|
<div className="roll-clock-side-panel">
|
||||||
<button
|
<button
|
||||||
className="roll-format-toggle"
|
className="roll-format-toggle"
|
||||||
onClick={() => setIs24Hour(!is24Hour)}
|
onClick={onToggleFormat}
|
||||||
>
|
>
|
||||||
{is24Hour ? '24H' : '12H'}
|
{is24Hour ? '24H' : '12H'}
|
||||||
</button>
|
</button>
|
||||||
@ -46,7 +44,7 @@ export function RollClock({ time, size, dateInfo, period }: RollClockProps) {
|
|||||||
<div className="roll-clock-main">
|
<div className="roll-clock-main">
|
||||||
<div className="roll-clock-header">
|
<div className="roll-clock-header">
|
||||||
<div className="roll-clock-header-left">
|
<div className="roll-clock-header-left">
|
||||||
{!is24Hour && period && (
|
{period && (
|
||||||
<span
|
<span
|
||||||
className="roll-clock-period"
|
className="roll-clock-period"
|
||||||
style={{ fontSize: `${periodSize}px` }}
|
style={{ fontSize: `${periodSize}px` }}
|
||||||
@ -55,12 +53,14 @@ export function RollClock({ time, size, dateInfo, period }: RollClockProps) {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{showDate && (
|
||||||
<span
|
<span
|
||||||
className="roll-clock-date"
|
className="roll-clock-date"
|
||||||
style={{ fontSize: `${dateSize}px` }}
|
style={{ fontSize: `${dateSize}px` }}
|
||||||
>
|
>
|
||||||
{dateInfo.dateString} {dateInfo.weekday}
|
{dateInfo.dateString} {dateInfo.weekday}
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="roll-clock-container">
|
<div className="roll-clock-container">
|
||||||
{/* Hours */}
|
{/* Hours */}
|
||||||
@ -101,6 +101,16 @@ export function RollClock({ time, size, dateInfo, period }: RollClockProps) {
|
|||||||
<RollDigit value={parseInt(seconds[1], 10)} size={size} direction={direction} />
|
<RollDigit value={parseInt(seconds[1], 10)} size={size} direction={direction} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{showTimezone && (
|
||||||
|
<div className="roll-clock-footer">
|
||||||
|
<span
|
||||||
|
className="roll-clock-timezone"
|
||||||
|
style={{ fontSize: `${timeZoneSize}px` }}
|
||||||
|
>
|
||||||
|
{timeZone}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Direction toggle button */}
|
{/* Direction toggle button */}
|
||||||
@ -112,7 +122,7 @@ export function RollClock({ time, size, dateInfo, period }: RollClockProps) {
|
|||||||
borderRadius: `${buttonSize / 2}px`,
|
borderRadius: `${buttonSize / 2}px`,
|
||||||
marginLeft: '8px',
|
marginLeft: '8px',
|
||||||
}}
|
}}
|
||||||
onClick={toggleDirection}
|
onClick={onToggleDirection}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="roll-clock-button-text"
|
className="roll-clock-button-text"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user