server-time-timer-yolabs-ui 1.0.11 → 1.0.13
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/README.md +48 -94
- package/dist/hooks/useServerTimer.d.ts +0 -1
- package/dist/hooks/useServerTimer.js +0 -1
- package/dist/ui/ServerTimerUI.js +2 -3
- package/dist/ui/components/TimerContainer.js +6 -26
- package/dist/ui/components/TimerUnit.js +41 -9
- package/dist/ui/types/index.d.ts +8 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,35 +1,33 @@
|
|
|
1
1
|
|
|
2
2
|
# ⏱️ server-time-timer-yolabs-ui
|
|
3
3
|
|
|
4
|
-
**SharePoint-styled React countdown timer UI built on top of `server-time-timer-yolabs
|
|
5
|
-
|
|
6
|
-
A **professional, enterprise-grade React UI** for **server-synchronized countdown timers**.
|
|
7
|
-
Designed for **accuracy, consistency, and corporate dashboards**.
|
|
4
|
+
**SharePoint-styled React countdown timer UI** built on top of `server-time-timer-yolabs`.
|
|
5
|
+
Enterprise-grade, **server-synchronized countdown timers** designed for accuracy, consistency, and corporate dashboards.
|
|
8
6
|
|
|
9
7
|
---
|
|
10
8
|
|
|
11
9
|
## 🚀 Why Use This UI?
|
|
12
10
|
|
|
13
|
-
Unlike traditional timers that rely on the **client’s
|
|
11
|
+
Unlike traditional timers that rely on the **client’s clock**, this UI uses **server time as the single source of truth**.
|
|
14
12
|
|
|
15
13
|
### ✅ Key Advantages
|
|
16
14
|
|
|
17
|
-
* 🔒
|
|
18
|
-
* 🌍
|
|
19
|
-
* 🧩
|
|
20
|
-
* ⚡
|
|
21
|
-
* 🕒
|
|
22
|
-
* 🏢
|
|
15
|
+
* 🔒 Server-trusted time → no client clock manipulation
|
|
16
|
+
* 🌍 Consistent countdown across all devices
|
|
17
|
+
* 🧩 Dynamic layouts, sizes, themes, and colors
|
|
18
|
+
* ⚡ Optional animated progress bar
|
|
19
|
+
* 🕒 Configurable units (days, hours, minutes, seconds)
|
|
20
|
+
* 🏢 Enterprise UI inspired by Fluent UI / SharePoint
|
|
23
21
|
|
|
24
22
|
---
|
|
25
23
|
|
|
26
|
-
## ⚡ Difference From
|
|
24
|
+
## ⚡ Difference From Normal Timers
|
|
27
25
|
|
|
28
26
|
| Feature | Normal Timers | server-time-timer-yolabs-ui |
|
|
29
27
|
| ------------------------ | --------------- | --------------------------------- |
|
|
30
28
|
| Time Source | Client | **Server (trusted)** |
|
|
31
29
|
| Cross-Device Consistency | ❌ Can differ | ✅ Always same |
|
|
32
|
-
|
|
|
30
|
+
| Drift Handling | ❌ None | ✅ Auto-corrected |
|
|
33
31
|
| Layouts | ❌ Fixed | ✅ Horizontal / Vertical / Compact |
|
|
34
32
|
| Unit Visibility | ❌ Fixed | ✅ Per-unit control |
|
|
35
33
|
| Progress Bar | ❌ Missing | ✅ Built-in |
|
|
@@ -60,29 +58,17 @@ import { ServerTimerUI } from 'server-time-timer-yolabs-ui';
|
|
|
60
58
|
export default function App() {
|
|
61
59
|
return (
|
|
62
60
|
<ServerTimerUI
|
|
63
|
-
serverNow="2025-12-25T12:00:00Z"
|
|
64
61
|
endTime="2025-12-25T12:10:00Z"
|
|
65
|
-
|
|
66
62
|
size="lg"
|
|
67
63
|
layout="horizontal"
|
|
68
64
|
theme="brand"
|
|
69
|
-
|
|
70
|
-
showProgress={true}
|
|
71
|
-
|
|
65
|
+
showUnits={{ days: true, hours: true, minutes: true, seconds: true }}
|
|
72
66
|
customColors={{
|
|
73
67
|
background: '#fef9f3',
|
|
74
68
|
text: '#323130',
|
|
75
69
|
unitBackground: '#fffbdd',
|
|
76
70
|
progress: '#0078d4'
|
|
77
71
|
}}
|
|
78
|
-
|
|
79
|
-
showUnits={{
|
|
80
|
-
days: true,
|
|
81
|
-
hours: true,
|
|
82
|
-
minutes: true,
|
|
83
|
-
seconds: true
|
|
84
|
-
}}
|
|
85
|
-
|
|
86
72
|
onEnd={() => alert('⏰ Time is up!')}
|
|
87
73
|
/>
|
|
88
74
|
);
|
|
@@ -95,9 +81,6 @@ export default function App() {
|
|
|
95
81
|
|
|
96
82
|
```ts
|
|
97
83
|
export interface ServerTimerUIProps {
|
|
98
|
-
/** Server UTC time (ISO string) */
|
|
99
|
-
serverNow: string;
|
|
100
|
-
|
|
101
84
|
/** Countdown end UTC time (ISO string) */
|
|
102
85
|
endTime: string;
|
|
103
86
|
|
|
@@ -110,11 +93,13 @@ export interface ServerTimerUIProps {
|
|
|
110
93
|
/** Built-in theme */
|
|
111
94
|
theme?: 'light' | 'dark' | 'brand';
|
|
112
95
|
|
|
113
|
-
/** Show
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
96
|
+
/** Show time units */
|
|
97
|
+
showUnits?: {
|
|
98
|
+
days?: boolean;
|
|
99
|
+
hours?: boolean;
|
|
100
|
+
minutes?: boolean;
|
|
101
|
+
seconds?: boolean;
|
|
102
|
+
};
|
|
118
103
|
|
|
119
104
|
/** Optional wrapper class */
|
|
120
105
|
className?: string;
|
|
@@ -127,13 +112,8 @@ export interface ServerTimerUIProps {
|
|
|
127
112
|
progress?: string;
|
|
128
113
|
};
|
|
129
114
|
|
|
130
|
-
/**
|
|
131
|
-
|
|
132
|
-
days?: boolean;
|
|
133
|
-
hours?: boolean;
|
|
134
|
-
minutes?: boolean;
|
|
135
|
-
seconds?: boolean;
|
|
136
|
-
};
|
|
115
|
+
/** Callback when timer ends */
|
|
116
|
+
onEnd?: () => void;
|
|
137
117
|
}
|
|
138
118
|
```
|
|
139
119
|
|
|
@@ -141,34 +121,17 @@ export interface ServerTimerUIProps {
|
|
|
141
121
|
|
|
142
122
|
## 🎨 Layouts
|
|
143
123
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
✔ Dashboards
|
|
148
|
-
✔ Desktop views
|
|
149
|
-
|
|
150
|
-
### Vertical
|
|
151
|
-
|
|
152
|
-
Units stacked vertically.
|
|
153
|
-
✔ Mobile
|
|
154
|
-
✔ Side panels
|
|
155
|
-
|
|
156
|
-
### Compact
|
|
157
|
-
|
|
158
|
-
Minimal, label-free layout.
|
|
159
|
-
✔ Cards
|
|
160
|
-
✔ Headers
|
|
161
|
-
✔ Space-constrained UIs
|
|
124
|
+
* **Horizontal (default)** – units in a row, ideal for dashboards and desktop views
|
|
125
|
+
* **Vertical** – stacked units, best for mobile or side panels
|
|
126
|
+
* **Compact** – minimal, label-free, fits in cards or headers
|
|
162
127
|
|
|
163
128
|
---
|
|
164
129
|
|
|
165
130
|
## 🎭 Themes
|
|
166
131
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
* `light` – light style
|
|
132
|
+
* `light` – Light UI
|
|
170
133
|
* `dark` – Enterprise dark mode
|
|
171
|
-
* `brand` – Corporate / SharePoint
|
|
134
|
+
* `brand` – Corporate / SharePoint styling
|
|
172
135
|
|
|
173
136
|
```tsx
|
|
174
137
|
<ServerTimerUI theme="dark" />
|
|
@@ -187,8 +150,8 @@ customColors={{
|
|
|
187
150
|
}}
|
|
188
151
|
```
|
|
189
152
|
|
|
190
|
-
|
|
191
|
-
|
|
153
|
+
* Overrides theme defaults
|
|
154
|
+
* Optional
|
|
192
155
|
|
|
193
156
|
---
|
|
194
157
|
|
|
@@ -203,27 +166,22 @@ showUnits={{
|
|
|
203
166
|
}}
|
|
204
167
|
```
|
|
205
168
|
|
|
206
|
-
|
|
207
|
-
|
|
169
|
+
* Hide unused units for a cleaner UI
|
|
170
|
+
* Useful for short timers
|
|
208
171
|
|
|
209
172
|
---
|
|
210
173
|
|
|
211
|
-
## 📱
|
|
174
|
+
## 📱 Responsive & Safe
|
|
212
175
|
|
|
213
176
|
* Uses `flex-wrap` to prevent overflow
|
|
214
|
-
* Auto-scales typography
|
|
215
|
-
* Vertical layout recommended for small screens
|
|
177
|
+
* Auto-scales typography based on `size`
|
|
216
178
|
* Numbers never jump or resize unexpectedly
|
|
217
|
-
|
|
218
|
-
✔ Mobile-safe
|
|
219
|
-
✔ Dashboard-safe
|
|
179
|
+
* Vertical layout recommended for small screens
|
|
220
180
|
|
|
221
181
|
---
|
|
222
182
|
|
|
223
183
|
## 🔁 Server Re-Sync (Long Timers)
|
|
224
184
|
|
|
225
|
-
For long-running timers, periodically resync with the server:
|
|
226
|
-
|
|
227
185
|
```ts
|
|
228
186
|
setInterval(async () => {
|
|
229
187
|
const res = await fetch('/api/server-time');
|
|
@@ -232,40 +190,36 @@ setInterval(async () => {
|
|
|
232
190
|
}, 30000);
|
|
233
191
|
```
|
|
234
192
|
|
|
235
|
-
|
|
236
|
-
|
|
193
|
+
* Prevents long-term drift
|
|
194
|
+
* Enterprise-grade accuracy
|
|
237
195
|
|
|
238
196
|
---
|
|
239
197
|
|
|
240
|
-
## 🔧
|
|
198
|
+
## 🔧 Framework-Agnostic Usage
|
|
241
199
|
|
|
242
|
-
|
|
200
|
+
Use the **core engine** (`server-time-timer-yolabs`) for Angular, Vue, or Vanilla JS:
|
|
243
201
|
|
|
244
202
|
```ts
|
|
245
203
|
import { createServerTimer } from 'server-time-timer-yolabs';
|
|
246
204
|
|
|
247
205
|
const timer = createServerTimer({
|
|
248
|
-
serverNow: new Date().toISOString(),
|
|
249
206
|
endTime: new Date(Date.now() + 10 * 60 * 1000).toISOString()
|
|
250
207
|
});
|
|
251
208
|
|
|
252
|
-
timer.onTick(
|
|
253
|
-
console.log(`${t.hours}h ${t.minutes}m ${t.seconds}s`);
|
|
254
|
-
});
|
|
255
|
-
|
|
209
|
+
timer.onTick(t => console.log(`${t.hours}h ${t.minutes}m ${t.seconds}s`));
|
|
256
210
|
timer.start();
|
|
257
211
|
```
|
|
258
212
|
|
|
259
|
-
|
|
260
|
-
|
|
213
|
+
* Wrap values in your own UI
|
|
214
|
+
* React UI package remains optional
|
|
261
215
|
|
|
262
216
|
---
|
|
263
217
|
|
|
264
|
-
## 🏢
|
|
218
|
+
## 🏢 Use Cases
|
|
265
219
|
|
|
266
220
|
* 🎟️ Lottery & raffle systems
|
|
267
221
|
* 🛒 Flash sales & promotions
|
|
268
|
-
* 📝 Online exams & quizzes
|
|
222
|
+
* 📝 Online exams & timed quizzes
|
|
269
223
|
* 🎮 Games & live events
|
|
270
224
|
* 📱 Telegram Mini Apps
|
|
271
225
|
* 🌐 Distributed enterprise dashboards
|
|
@@ -275,17 +229,17 @@ timer.start();
|
|
|
275
229
|
|
|
276
230
|
## 📜 License
|
|
277
231
|
|
|
278
|
-
**
|
|
279
|
-
© **YoLabs**
|
|
232
|
+
**YonasLabs / YoLabs**
|
|
280
233
|
|
|
281
234
|
---
|
|
282
235
|
|
|
283
|
-
## ✅
|
|
236
|
+
## ✅ Notes
|
|
284
237
|
|
|
285
|
-
|
|
238
|
+
Designed for **mission-critical timers**:
|
|
286
239
|
|
|
287
240
|
* Accuracy matters
|
|
288
|
-
* Client manipulation
|
|
289
|
-
* UI consistency
|
|
241
|
+
* Client manipulation is prevented
|
|
242
|
+
* UI consistency across platforms is guaranteed
|
|
290
243
|
|
|
291
244
|
---
|
|
245
|
+
|
package/dist/ui/ServerTimerUI.js
CHANGED
|
@@ -7,9 +7,8 @@ exports.ServerTimerUI = void 0;
|
|
|
7
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
8
|
const TimerContainer_1 = __importDefault(require("./components/TimerContainer"));
|
|
9
9
|
const TimerUnit_1 = __importDefault(require("./components/TimerUnit"));
|
|
10
|
-
const TimerProgress_1 = __importDefault(require("./components/TimerProgress"));
|
|
11
10
|
const useServerTimer_1 = require("../hooks/useServerTimer");
|
|
12
|
-
const ServerTimerUI = ({ endTime, size = 'md', layout = 'horizontal', theme = 'light',
|
|
11
|
+
const ServerTimerUI = ({ endTime, size = 'md', layout = 'horizontal', theme = 'light', customColors = {}, showUnits = { days: true, hours: true, minutes: true, seconds: true }, onEnd, className, }) => {
|
|
13
12
|
const time = (0, useServerTimer_1.useServerTimer)(endTime, onEnd);
|
|
14
13
|
const themeColors = {
|
|
15
14
|
light: { background: '#ffffff', text: '#323130', unitBackground: '#f3f2f1', progress: '#0078d4' },
|
|
@@ -17,6 +16,6 @@ const ServerTimerUI = ({ endTime, size = 'md', layout = 'horizontal', theme = 'l
|
|
|
17
16
|
brand: { background: '#fef9f3', text: '#323130', unitBackground: '#fffbdd', progress: '#0078d4' },
|
|
18
17
|
};
|
|
19
18
|
const colors = Object.assign(Object.assign({}, themeColors[theme]), customColors);
|
|
20
|
-
return ((0, jsx_runtime_1.jsxs)(TimerContainer_1.default, { size: size, layout: layout, background: colors.background, color: colors.text, className: className, children: [showUnits.days && (0, jsx_runtime_1.jsx)(TimerUnit_1.default, { label: "Days", value: time.days, size: size, background: colors.unitBackground, color: colors.text
|
|
19
|
+
return ((0, jsx_runtime_1.jsxs)(TimerContainer_1.default, { size: size, layout: layout, background: colors.background, color: colors.text, className: className, children: [showUnits.days && (0, jsx_runtime_1.jsx)(TimerUnit_1.default, { label: "Days", value: time.days, size: size, background: colors.unitBackground, color: colors.text }), showUnits.hours && (0, jsx_runtime_1.jsx)(TimerUnit_1.default, { label: "Hours", value: time.hours, size: size, background: colors.unitBackground, color: colors.text }), showUnits.minutes && (0, jsx_runtime_1.jsx)(TimerUnit_1.default, { label: "Minutes", value: time.minutes, size: size, background: colors.unitBackground, color: colors.text }), showUnits.seconds && (0, jsx_runtime_1.jsx)(TimerUnit_1.default, { label: "Seconds", value: time.seconds, size: size, background: colors.unitBackground, color: colors.text })] }));
|
|
21
20
|
};
|
|
22
21
|
exports.ServerTimerUI = ServerTimerUI;
|
|
@@ -1,37 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const paddingMap = {
|
|
5
|
-
sm: '0.5rem 1rem',
|
|
6
|
-
md: '1rem 1.5rem',
|
|
7
|
-
lg: '1.5rem 2rem',
|
|
8
|
-
};
|
|
9
4
|
const gapMap = {
|
|
10
5
|
horizontal: '0.625rem',
|
|
11
6
|
vertical: '0.625rem',
|
|
12
7
|
compact: '0.25rem',
|
|
13
8
|
};
|
|
14
9
|
const TimerContainer = ({ children, size = 'md', className = '', background = '#ffffff', color = '#323130', layout = 'horizontal', }) => {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
22
|
-
case 'compact':
|
|
23
|
-
return {
|
|
24
|
-
flexDirection: 'row',
|
|
25
|
-
flexWrap: 'wrap',
|
|
26
|
-
};
|
|
27
|
-
default: // horizontal
|
|
28
|
-
return {
|
|
29
|
-
flexDirection: 'row',
|
|
30
|
-
flexWrap: 'nowrap',
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
const flexStyles = getFlexStyles();
|
|
35
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: `timer-container ${className}`, style: Object.assign(Object.assign({ display: 'flex' }, flexStyles), { alignItems: 'center', justifyContent: 'center', gap: gapMap[layout], backgroundColor: background, color: color, border: '1px solid #e1e1e1', borderRadius: '4px', padding: paddingMap[size], fontFamily: `'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif`, fontSize: '1rem', overflow: 'hidden', transition: 'all 0.2s ease', boxSizing: 'border-box', minWidth: layout === 'compact' ? 'fit-content' : undefined }), role: "timer", "aria-label": "timer container", children: children }));
|
|
10
|
+
const flexStyles = {
|
|
11
|
+
horizontal: { flexDirection: 'row', flexWrap: 'nowrap' },
|
|
12
|
+
vertical: { flexDirection: 'column', flexWrap: 'nowrap' },
|
|
13
|
+
compact: { flexDirection: 'row', flexWrap: 'wrap' },
|
|
14
|
+
}[layout]; // <--- cast here
|
|
15
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: `timer-container ${className}`, style: Object.assign(Object.assign({ display: 'flex' }, flexStyles), { alignItems: 'center', justifyContent: 'center', gap: gapMap[layout], backgroundColor: background, color: color, border: '1px solid #e1e1e1', borderRadius: '6px', padding: size === 'sm' ? '0.25rem' : size === 'md' ? '0.5rem' : '0.75rem', fontFamily: `'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif`, fontSize: '1rem', overflow: 'hidden', boxSizing: 'border-box', transition: 'all 0.2s ease' }), role: "timer", "aria-label": "timer container", children: children }));
|
|
36
16
|
};
|
|
37
17
|
exports.default = TimerContainer;
|
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const fontSizeMap = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
const fontSizeMap = {
|
|
5
|
+
sm: '0.75rem', // label font
|
|
6
|
+
md: '0.875rem',
|
|
7
|
+
lg: '1rem',
|
|
8
|
+
};
|
|
9
|
+
const numberFontMap = {
|
|
10
|
+
sm: '1rem',
|
|
11
|
+
md: '1.25rem',
|
|
12
|
+
lg: '1.5rem',
|
|
13
|
+
};
|
|
14
|
+
const paddingMap = {
|
|
15
|
+
sm: '0.25rem 0.5rem',
|
|
16
|
+
md: '0.5rem 0.75rem',
|
|
17
|
+
lg: '0.75rem 1rem',
|
|
18
|
+
};
|
|
19
|
+
const TimerUnit = ({ label, value, size = 'md', background = '#f3f2f1', color = '#323130', numberSize = 'md', }) => {
|
|
20
|
+
return ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
21
|
+
display: 'flex',
|
|
22
|
+
flexDirection: 'column',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
backgroundColor: background,
|
|
26
|
+
color: color,
|
|
27
|
+
borderRadius: '6px',
|
|
28
|
+
padding: paddingMap[size],
|
|
29
|
+
minWidth: size === 'sm' ? '3rem' : size === 'md' ? '4rem' : '5rem',
|
|
30
|
+
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
|
|
31
|
+
transition: 'all 0.2s ease',
|
|
32
|
+
boxSizing: 'border-box',
|
|
33
|
+
fontFamily: `'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif`,
|
|
34
|
+
}, children: [(0, jsx_runtime_1.jsx)("span", { style: {
|
|
35
|
+
fontSize: numberFontMap[numberSize],
|
|
36
|
+
fontWeight: 600,
|
|
37
|
+
lineHeight: 1,
|
|
38
|
+
}, children: value.toString().padStart(2, '0') }), (0, jsx_runtime_1.jsx)("span", { style: {
|
|
39
|
+
fontSize: fontSizeMap[size],
|
|
40
|
+
marginTop: '0.25rem',
|
|
41
|
+
textTransform: 'uppercase',
|
|
42
|
+
whiteSpace: 'nowrap',
|
|
43
|
+
}, children: label })] }));
|
|
44
|
+
};
|
|
13
45
|
exports.default = TimerUnit;
|
package/dist/ui/types/index.d.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
export type TimerSize = 'sm' | 'md' | 'lg';
|
|
2
2
|
export type TimerLayout = 'horizontal' | 'vertical' | 'compact';
|
|
3
3
|
export type TimerTheme = 'light' | 'dark' | 'brand';
|
|
4
|
-
export interface
|
|
5
|
-
background?: string;
|
|
6
|
-
text?: string;
|
|
7
|
-
unitBackground?: string;
|
|
8
|
-
progress?: string;
|
|
9
|
-
}
|
|
10
|
-
export interface ShowUnits {
|
|
4
|
+
export interface TimerUnits {
|
|
11
5
|
days?: boolean;
|
|
12
6
|
hours?: boolean;
|
|
13
7
|
minutes?: boolean;
|
|
@@ -18,10 +12,12 @@ export interface ServerTimerUIProps {
|
|
|
18
12
|
size?: TimerSize;
|
|
19
13
|
layout?: TimerLayout;
|
|
20
14
|
theme?: TimerTheme;
|
|
21
|
-
|
|
22
|
-
customColors?: CustomColors;
|
|
23
|
-
showUnits?: ShowUnits;
|
|
24
|
-
onEnd?: () => void;
|
|
15
|
+
showUnits?: TimerUnits;
|
|
25
16
|
className?: string;
|
|
26
|
-
|
|
17
|
+
customColors?: {
|
|
18
|
+
background?: string;
|
|
19
|
+
text?: string;
|
|
20
|
+
unitBackground?: string;
|
|
21
|
+
};
|
|
22
|
+
onEnd?: () => void;
|
|
27
23
|
}
|
package/package.json
CHANGED