@procaaso/alphinity-ui-components 1.0.0

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/dist/index.cjs ADDED
@@ -0,0 +1,1084 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Button: () => Button,
24
+ ControlPanel: () => ControlPanel,
25
+ Selector: () => Selector,
26
+ StatusIndicator: () => StatusIndicator,
27
+ ThemeProvider: () => ThemeProvider,
28
+ ThemeToggle: () => ThemeToggle,
29
+ ValueEntry: () => ValueEntry,
30
+ useTheme: () => useTheme
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/Button.tsx
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var Button = ({
37
+ variant = "primary",
38
+ children,
39
+ className = "",
40
+ ...props
41
+ }) => {
42
+ const baseStyles = "h-20 min-w-[160px] px-12 py-5 rounded-3xl font-bold text-2xl transition-all duration-300 transform hover:scale-[1.03] active:scale-[0.97] focus:outline-none focus:ring-4 focus:ring-offset-3 focus:ring-offset-slate-900 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none shadow-2xl backdrop-blur-sm";
43
+ const variantStyles = variant === "primary" ? "bg-gradient-to-br from-blue-400 via-blue-600 to-purple-700 hover:from-blue-500 hover:via-blue-700 hover:to-purple-800 text-white shadow-blue-600/40 focus:ring-blue-400/70 border border-blue-300/40 hover:shadow-2xl hover:shadow-blue-600/60 hover:border-blue-200/50" : "bg-gradient-to-br from-slate-500 via-slate-700 to-slate-900 hover:from-slate-400 hover:via-slate-600 hover:to-slate-800 text-slate-100 shadow-slate-600/40 focus:ring-slate-400/70 border border-slate-400/40 hover:shadow-2xl hover:shadow-slate-600/60 hover:border-slate-300/50";
44
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: `${baseStyles} ${variantStyles} ${className}`.trim(), ...props, children });
45
+ };
46
+
47
+ // src/TestValueEntry.tsx
48
+ var import_react = require("react");
49
+ var import_jsx_runtime2 = require("react/jsx-runtime");
50
+ var ValueEntry = ({
51
+ value,
52
+ onChange,
53
+ unit,
54
+ status = "normal",
55
+ min,
56
+ max,
57
+ precision = 2,
58
+ inputType = "numeric",
59
+ label,
60
+ readOnly = false,
61
+ className = "",
62
+ disabled,
63
+ ...props
64
+ }) => {
65
+ const [displayValue, setDisplayValue] = (0, import_react.useState)(String(value));
66
+ const [isEditing, setIsEditing] = (0, import_react.useState)(false);
67
+ const handleFocus = (0, import_react.useCallback)(() => {
68
+ setIsEditing(true);
69
+ setDisplayValue(String(value));
70
+ }, [value]);
71
+ const handleBlur = (0, import_react.useCallback)(() => {
72
+ setIsEditing(false);
73
+ let newValue = displayValue;
74
+ if (inputType === "numeric") {
75
+ const numValue = parseFloat(displayValue);
76
+ if (!isNaN(numValue)) {
77
+ let constrainedValue = numValue;
78
+ if (min !== void 0) constrainedValue = Math.max(min, constrainedValue);
79
+ if (max !== void 0) constrainedValue = Math.min(max, constrainedValue);
80
+ constrainedValue = parseFloat(constrainedValue.toFixed(precision));
81
+ newValue = constrainedValue;
82
+ setDisplayValue(String(constrainedValue));
83
+ } else {
84
+ setDisplayValue(String(value));
85
+ return;
86
+ }
87
+ }
88
+ if (newValue !== value) {
89
+ onChange(newValue);
90
+ }
91
+ }, [displayValue, inputType, min, max, precision, value, onChange]);
92
+ const handleChange = (0, import_react.useCallback)((e) => {
93
+ setDisplayValue(e.target.value);
94
+ }, []);
95
+ const handleKeyDown = (0, import_react.useCallback)(
96
+ (e) => {
97
+ if (e.key === "Enter") {
98
+ e.currentTarget.blur();
99
+ }
100
+ if (e.key === "Escape") {
101
+ setDisplayValue(String(value));
102
+ e.currentTarget.blur();
103
+ }
104
+ },
105
+ [value]
106
+ );
107
+ const displayText = isEditing ? displayValue : inputType === "numeric" ? Number(value).toFixed(precision) : String(value);
108
+ const getStatusStyles = () => {
109
+ switch (status) {
110
+ case "alarm":
111
+ return {
112
+ borderColor: "#ef4444",
113
+ backgroundColor: "rgba(239, 68, 68, 0.15)",
114
+ color: "#fecaca",
115
+ boxShadow: "0 0 20px rgba(239, 68, 68, 0.3)"
116
+ };
117
+ case "warning":
118
+ return {
119
+ borderColor: "#f59e0b",
120
+ backgroundColor: "rgba(245, 158, 11, 0.15)",
121
+ color: "#fed7aa",
122
+ boxShadow: "0 0 20px rgba(245, 158, 11, 0.3)"
123
+ };
124
+ case "disabled":
125
+ return {
126
+ borderColor: "#6b7280",
127
+ backgroundColor: "rgba(107, 114, 128, 0.1)",
128
+ color: "#9ca3af",
129
+ cursor: "not-allowed"
130
+ };
131
+ default:
132
+ return {
133
+ borderColor: "#64748b",
134
+ backgroundColor: "rgba(30, 41, 59, 0.9)",
135
+ color: "#f1f5f9",
136
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.3)"
137
+ };
138
+ }
139
+ };
140
+ const statusStyles = getStatusStyles();
141
+ const inputStyle = {
142
+ width: "12rem",
143
+ height: "4rem",
144
+ padding: unit ? "1rem 4rem 1rem 1.5rem" : "1rem 1.5rem",
145
+ border: "2px solid",
146
+ borderRadius: "1.5rem",
147
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
148
+ fontSize: "1.25rem",
149
+ fontWeight: "bold",
150
+ textAlign: "center",
151
+ outline: "none",
152
+ transition: "all 300ms ease",
153
+ backdropFilter: "blur(8px)",
154
+ ...statusStyles
155
+ };
156
+ const inputStyleFocus = {
157
+ ...inputStyle,
158
+ borderColor: "#60a5fa",
159
+ boxShadow: "0 0 0 4px rgba(96, 165, 250, 0.3), " + (statusStyles.boxShadow || "0 8px 32px rgba(0, 0, 0, 0.3)"),
160
+ transform: "scale(1.02)"
161
+ };
162
+ const unitStyle = {
163
+ position: "absolute",
164
+ right: "0.75rem",
165
+ top: "50%",
166
+ transform: "translateY(-50%)",
167
+ padding: "0.5rem 0.75rem",
168
+ fontSize: "1rem",
169
+ fontWeight: "bold",
170
+ color: "#ffffff",
171
+ backgroundColor: "#3b82f6",
172
+ borderRadius: "0.5rem",
173
+ border: "2px solid #60a5fa",
174
+ backdropFilter: "blur(4px)",
175
+ pointerEvents: "none",
176
+ zIndex: 10,
177
+ boxShadow: "0 2px 8px rgba(59, 130, 246, 0.4)"
178
+ };
179
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
180
+ "div",
181
+ {
182
+ style: {
183
+ display: "inline-flex",
184
+ flexDirection: "column",
185
+ alignItems: "center",
186
+ gap: "0.75rem"
187
+ },
188
+ children: [
189
+ label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
190
+ "label",
191
+ {
192
+ style: {
193
+ fontSize: "0.875rem",
194
+ fontWeight: "600",
195
+ color: "#cbd5e1",
196
+ textTransform: "uppercase",
197
+ letterSpacing: "0.05em"
198
+ },
199
+ children: label
200
+ }
201
+ ),
202
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative", display: "inline-flex", alignItems: "center" }, children: [
203
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
204
+ "input",
205
+ {
206
+ type: "text",
207
+ value: displayText,
208
+ onChange: handleChange,
209
+ onFocus: (e) => {
210
+ Object.assign(e.target.style, inputStyleFocus);
211
+ handleFocus();
212
+ },
213
+ onBlur: (e) => {
214
+ Object.assign(e.target.style, inputStyle);
215
+ handleBlur();
216
+ },
217
+ onKeyDown: handleKeyDown,
218
+ disabled,
219
+ readOnly,
220
+ style: inputStyle,
221
+ ...props
222
+ }
223
+ ),
224
+ unit && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: unitStyle, children: unit }),
225
+ status === "alarm" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
226
+ "div",
227
+ {
228
+ style: {
229
+ position: "absolute",
230
+ top: "-8px",
231
+ right: "-8px",
232
+ width: "1rem",
233
+ height: "1rem",
234
+ backgroundColor: "#ef4444",
235
+ borderRadius: "50%",
236
+ boxShadow: "0 0 10px rgba(239, 68, 68, 0.8)",
237
+ animation: "pulse 2s infinite"
238
+ }
239
+ }
240
+ ),
241
+ status === "warning" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
242
+ "div",
243
+ {
244
+ style: {
245
+ position: "absolute",
246
+ top: "-8px",
247
+ right: "-8px",
248
+ width: "1rem",
249
+ height: "1rem",
250
+ backgroundColor: "#f59e0b",
251
+ borderRadius: "50%",
252
+ boxShadow: "0 0 10px rgba(245, 158, 11, 0.8)"
253
+ }
254
+ }
255
+ )
256
+ ] })
257
+ ]
258
+ }
259
+ );
260
+ };
261
+
262
+ // src/StatusIndicator.tsx
263
+ var import_jsx_runtime3 = require("react/jsx-runtime");
264
+ var StatusIndicator = ({
265
+ status,
266
+ size = "medium",
267
+ animation = "none",
268
+ label,
269
+ labelPosition = "right",
270
+ shape = "circle",
271
+ led = true,
272
+ className = "",
273
+ ...props
274
+ }) => {
275
+ const sizeStyles = {
276
+ small: "w-5 h-5",
277
+ medium: "w-7 h-7",
278
+ large: "w-10 h-10"
279
+ };
280
+ const shapeStyles = {
281
+ circle: "rounded-full",
282
+ square: "rounded-lg",
283
+ diamond: "rotate-45 rounded-lg"
284
+ };
285
+ const statusStyles = {
286
+ off: "bg-gradient-to-br from-slate-500 to-slate-600 border-slate-400 shadow-slate-500/30",
287
+ normal: "bg-gradient-to-br from-emerald-400 via-green-500 to-green-600 border-emerald-300 shadow-green-500/70",
288
+ alarm: "bg-gradient-to-br from-red-400 via-red-500 to-red-600 border-red-300 shadow-red-500/70",
289
+ warning: "bg-gradient-to-br from-amber-400 via-yellow-500 to-orange-500 border-amber-300 shadow-yellow-500/70",
290
+ active: "bg-gradient-to-br from-blue-400 via-blue-500 to-indigo-600 border-blue-300 shadow-blue-500/70",
291
+ fault: "bg-gradient-to-br from-purple-400 via-purple-500 to-violet-600 border-purple-300 shadow-purple-500/70"
292
+ };
293
+ const animationStyles = {
294
+ none: "",
295
+ pulse: "animate-pulse",
296
+ blink: "animate-bounce",
297
+ flash: "animate-ping"
298
+ };
299
+ const ledEffect = led ? "shadow-lg border-2" : "border";
300
+ const glowEffect = status !== "off" && led ? "shadow-lg" : "";
301
+ const indicatorClasses = `
302
+ ${sizeStyles[size]}
303
+ ${shapeStyles[shape]}
304
+ ${statusStyles[status]}
305
+ ${animationStyles[animation]}
306
+ ${ledEffect}
307
+ ${glowEffect}
308
+ inline-block
309
+ transition-all
310
+ duration-300
311
+ `.trim().replace(/\s+/g, " ");
312
+ const labelClasses = "text-sm font-medium text-gray-700 uppercase tracking-wide select-none";
313
+ const getContainerLayout = () => {
314
+ switch (labelPosition) {
315
+ case "left":
316
+ return "inline-flex items-center gap-2 flex-row-reverse";
317
+ case "right":
318
+ return "inline-flex items-center gap-2";
319
+ case "top":
320
+ return "inline-flex flex-col-reverse items-center gap-1";
321
+ case "bottom":
322
+ return "inline-flex flex-col items-center gap-1";
323
+ default:
324
+ return "inline-flex items-center gap-2";
325
+ }
326
+ };
327
+ const getAriaLabel = () => {
328
+ if (label) {
329
+ return `${label}: ${status}`;
330
+ }
331
+ return `Status indicator: ${status}`;
332
+ };
333
+ const getAriaDescription = () => {
334
+ const descriptions = {
335
+ off: "Inactive or disabled state",
336
+ normal: "Normal operating condition",
337
+ alarm: "Critical alarm condition requiring attention",
338
+ warning: "Warning condition that should be monitored",
339
+ active: "Currently active or running",
340
+ fault: "System fault or error condition"
341
+ };
342
+ return descriptions[status];
343
+ };
344
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `${getContainerLayout()} ${className}`.trim(), ...props, children: [
345
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
346
+ "div",
347
+ {
348
+ className: indicatorClasses,
349
+ role: "status",
350
+ "aria-label": getAriaLabel(),
351
+ "aria-description": getAriaDescription(),
352
+ "data-status": status,
353
+ "data-testid": `status-indicator-${status}`
354
+ }
355
+ ),
356
+ label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: labelClasses, children: label })
357
+ ] });
358
+ };
359
+
360
+ // src/Selector.tsx
361
+ var import_react2 = require("react");
362
+ var import_jsx_runtime4 = require("react/jsx-runtime");
363
+ var Selector = ({
364
+ value,
365
+ onChange,
366
+ options,
367
+ placeholder = "Select option...",
368
+ disabled = false,
369
+ status = "normal",
370
+ size = "medium",
371
+ label,
372
+ dropdownUp = false,
373
+ className = "",
374
+ ...props
375
+ }) => {
376
+ const [isOpen, setIsOpen] = (0, import_react2.useState)(false);
377
+ const [focusedIndex, setFocusedIndex] = (0, import_react2.useState)(-1);
378
+ const selectorRef = (0, import_react2.useRef)(null);
379
+ const dropdownRef = (0, import_react2.useRef)(null);
380
+ (0, import_react2.useEffect)(() => {
381
+ const handleClickOutside = (event) => {
382
+ if (selectorRef.current && !selectorRef.current.contains(event.target)) {
383
+ setIsOpen(false);
384
+ setFocusedIndex(-1);
385
+ }
386
+ };
387
+ document.addEventListener("mousedown", handleClickOutside);
388
+ return () => document.removeEventListener("mousedown", handleClickOutside);
389
+ }, []);
390
+ const handleKeyDown = (e) => {
391
+ if (disabled) return;
392
+ switch (e.key) {
393
+ case "Enter":
394
+ case " ":
395
+ e.preventDefault();
396
+ if (!isOpen) {
397
+ setIsOpen(true);
398
+ setFocusedIndex(
399
+ Math.max(
400
+ 0,
401
+ options.findIndex((opt) => opt.value === value)
402
+ )
403
+ );
404
+ } else if (focusedIndex >= 0) {
405
+ const selectedOption2 = options[focusedIndex];
406
+ if (!selectedOption2.disabled) {
407
+ onChange(selectedOption2.value);
408
+ setIsOpen(false);
409
+ setFocusedIndex(-1);
410
+ }
411
+ }
412
+ break;
413
+ case "Escape":
414
+ setIsOpen(false);
415
+ setFocusedIndex(-1);
416
+ break;
417
+ case "ArrowDown":
418
+ e.preventDefault();
419
+ if (!isOpen) {
420
+ setIsOpen(true);
421
+ setFocusedIndex(
422
+ Math.max(
423
+ 0,
424
+ options.findIndex((opt) => opt.value === value)
425
+ )
426
+ );
427
+ } else {
428
+ setFocusedIndex((prev) => {
429
+ const nextIndex = Math.min(prev + 1, options.length - 1);
430
+ return options[nextIndex]?.disabled ? prev : nextIndex;
431
+ });
432
+ }
433
+ break;
434
+ case "ArrowUp":
435
+ e.preventDefault();
436
+ if (isOpen) {
437
+ setFocusedIndex((prev) => {
438
+ const nextIndex = Math.max(prev - 1, 0);
439
+ return options[nextIndex]?.disabled ? prev : nextIndex;
440
+ });
441
+ }
442
+ break;
443
+ }
444
+ };
445
+ const selectedOption = options.find((opt) => opt.value === value);
446
+ const getSizeStyles = () => {
447
+ switch (size) {
448
+ case "small":
449
+ return {
450
+ height: "2.5rem",
451
+ padding: "0.5rem 0.75rem",
452
+ fontSize: "0.875rem",
453
+ iconSize: "1rem"
454
+ };
455
+ case "large":
456
+ return {
457
+ height: "4rem",
458
+ padding: "1rem 1.5rem",
459
+ fontSize: "1.25rem",
460
+ iconSize: "1.5rem"
461
+ };
462
+ default:
463
+ return {
464
+ height: "3rem",
465
+ padding: "0.75rem 1rem",
466
+ fontSize: "1rem",
467
+ iconSize: "1.25rem"
468
+ };
469
+ }
470
+ };
471
+ const getStatusStyles = () => {
472
+ switch (status) {
473
+ case "alarm":
474
+ return {
475
+ borderColor: "var(--selector-alarm-border, #ef4444)",
476
+ backgroundColor: "var(--selector-alarm-bg, rgba(239, 68, 68, 0.15))",
477
+ color: "var(--selector-alarm-text, #fecaca)",
478
+ boxShadow: "var(--selector-alarm-shadow, 0 0 20px rgba(239, 68, 68, 0.3))"
479
+ };
480
+ case "warning":
481
+ return {
482
+ borderColor: "var(--selector-warning-border, #f59e0b)",
483
+ backgroundColor: "var(--selector-warning-bg, rgba(245, 158, 11, 0.15))",
484
+ color: "var(--selector-warning-text, #fed7aa)",
485
+ boxShadow: "var(--selector-warning-shadow, 0 0 20px rgba(245, 158, 11, 0.3))"
486
+ };
487
+ case "disabled":
488
+ return {
489
+ borderColor: "var(--selector-disabled-border, #6b7280)",
490
+ backgroundColor: "var(--selector-disabled-bg, rgba(107, 114, 128, 0.1))",
491
+ color: "var(--selector-disabled-text, #9ca3af)",
492
+ cursor: "not-allowed"
493
+ };
494
+ default:
495
+ return {
496
+ borderColor: "var(--selector-normal-border, #64748b)",
497
+ backgroundColor: "var(--selector-normal-bg, rgba(30, 41, 59, 0.9))",
498
+ color: "var(--selector-normal-text, #f1f5f9)",
499
+ boxShadow: "var(--selector-normal-shadow, 0 8px 32px rgba(0, 0, 0, 0.3))"
500
+ };
501
+ }
502
+ };
503
+ const sizeStyles = getSizeStyles();
504
+ const statusStyles = getStatusStyles();
505
+ const selectorStyle = {
506
+ position: "relative",
507
+ width: "100%",
508
+ height: sizeStyles.height,
509
+ padding: sizeStyles.padding,
510
+ paddingRight: "3rem",
511
+ border: "2px solid",
512
+ borderRadius: "1rem",
513
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
514
+ fontSize: sizeStyles.fontSize,
515
+ fontWeight: "bold",
516
+ outline: "none",
517
+ transition: "all 300ms ease",
518
+ backdropFilter: "blur(8px)",
519
+ cursor: disabled ? "not-allowed" : "pointer",
520
+ display: "flex",
521
+ alignItems: "center",
522
+ justifyContent: "space-between",
523
+ userSelect: "none",
524
+ ...statusStyles
525
+ };
526
+ const dropdownStyle = {
527
+ position: "absolute",
528
+ top: dropdownUp ? "auto" : "100%",
529
+ bottom: dropdownUp ? "100%" : "auto",
530
+ left: 0,
531
+ right: 0,
532
+ marginTop: dropdownUp ? 0 : "0.25rem",
533
+ marginBottom: dropdownUp ? "0.25rem" : 0,
534
+ backgroundColor: "var(--selector-dropdown-bg, rgba(30, 41, 59, 0.95))",
535
+ border: "2px solid var(--selector-dropdown-border, #64748b)",
536
+ borderRadius: "1rem",
537
+ backdropFilter: "blur(12px)",
538
+ boxShadow: "var(--selector-dropdown-shadow, 0 12px 48px rgba(0, 0, 0, 0.4))",
539
+ zIndex: 50,
540
+ maxHeight: "12rem",
541
+ overflowY: "auto"
542
+ };
543
+ const optionStyle = (option, index) => ({
544
+ padding: sizeStyles.padding,
545
+ fontSize: sizeStyles.fontSize,
546
+ fontWeight: "600",
547
+ color: option.disabled ? "var(--selector-option-disabled-text, #6b7280)" : "var(--selector-option-text, #f1f5f9)",
548
+ backgroundColor: index === focusedIndex ? "var(--selector-option-focused-bg, rgba(59, 130, 246, 0.3))" : "transparent",
549
+ cursor: option.disabled ? "not-allowed" : "pointer",
550
+ transition: "all 150ms ease",
551
+ borderRadius: index === focusedIndex ? "0.5rem" : "0",
552
+ margin: index === focusedIndex ? "0.125rem" : "0"
553
+ });
554
+ const arrowStyle = {
555
+ position: "absolute",
556
+ right: "1rem",
557
+ top: "50%",
558
+ transform: `translateY(-50%) rotate(${isOpen ? "180deg" : "0deg"})`,
559
+ transition: "transform 200ms ease",
560
+ width: sizeStyles.iconSize,
561
+ height: sizeStyles.iconSize,
562
+ color: disabled ? "#6b7280" : "#94a3b8"
563
+ };
564
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
565
+ "div",
566
+ {
567
+ className: "selector-container",
568
+ style: { display: "flex", flexDirection: "column", gap: "0.5rem" },
569
+ children: [
570
+ label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
571
+ "label",
572
+ {
573
+ className: "selector-label",
574
+ style: {
575
+ fontSize: "0.875rem",
576
+ fontWeight: "600",
577
+ color: "var(--selector-label-color, #cbd5e1)",
578
+ textTransform: "uppercase",
579
+ letterSpacing: "0.05em"
580
+ },
581
+ children: label
582
+ }
583
+ ),
584
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { ref: selectorRef, style: { position: "relative" }, children: [
585
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
586
+ "div",
587
+ {
588
+ className: `selector ${className}`,
589
+ style: selectorStyle,
590
+ onClick: () => !disabled && setIsOpen(!isOpen),
591
+ onKeyDown: handleKeyDown,
592
+ tabIndex: disabled ? -1 : 0,
593
+ role: "combobox",
594
+ "aria-expanded": isOpen,
595
+ "aria-haspopup": "listbox",
596
+ ...props,
597
+ children: [
598
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
599
+ "span",
600
+ {
601
+ style: {
602
+ color: selectedOption ? statusStyles.color : "var(--selector-placeholder-text, #9ca3af)",
603
+ overflow: "hidden",
604
+ textOverflow: "ellipsis",
605
+ whiteSpace: "nowrap"
606
+ },
607
+ children: selectedOption ? selectedOption.label : placeholder
608
+ }
609
+ ),
610
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { style: arrowStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })
611
+ ]
612
+ }
613
+ ),
614
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: dropdownRef, style: dropdownStyle, role: "listbox", children: options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
615
+ "div",
616
+ {
617
+ style: optionStyle(option, index),
618
+ onClick: () => {
619
+ if (!option.disabled) {
620
+ onChange(option.value);
621
+ setIsOpen(false);
622
+ setFocusedIndex(-1);
623
+ }
624
+ },
625
+ onMouseEnter: () => !option.disabled && setFocusedIndex(index),
626
+ role: "option",
627
+ "aria-selected": option.value === value,
628
+ "aria-disabled": option.disabled,
629
+ title: option.description,
630
+ children: [
631
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
632
+ "div",
633
+ {
634
+ style: { display: "flex", justifyContent: "space-between", alignItems: "center" },
635
+ children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: option.label }),
637
+ option.value === value && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
638
+ "svg",
639
+ {
640
+ style: { width: "1rem", height: "1rem", color: "#3b82f6" },
641
+ fill: "currentColor",
642
+ viewBox: "0 0 20 20",
643
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
644
+ "path",
645
+ {
646
+ fillRule: "evenodd",
647
+ d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
648
+ clipRule: "evenodd"
649
+ }
650
+ )
651
+ }
652
+ )
653
+ ]
654
+ }
655
+ ),
656
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
657
+ "div",
658
+ {
659
+ style: {
660
+ fontSize: "0.75rem",
661
+ color: "#9ca3af",
662
+ marginTop: "0.25rem",
663
+ fontWeight: "normal"
664
+ },
665
+ children: option.description
666
+ }
667
+ )
668
+ ]
669
+ },
670
+ option.value
671
+ )) })
672
+ ] })
673
+ ]
674
+ }
675
+ );
676
+ };
677
+
678
+ // src/ControlPanel.tsx
679
+ var import_react3 = require("react");
680
+ var import_jsx_runtime5 = require("react/jsx-runtime");
681
+ var ControlPanel = ({
682
+ title,
683
+ selectedMode,
684
+ modeOptions,
685
+ onModeChange,
686
+ setpointValue,
687
+ onSetpointChange,
688
+ secondSetpointValue,
689
+ onSecondSetpointChange,
690
+ modeConfigs = {},
691
+ defaultUnit = "Units",
692
+ defaultLabel = "Setpoint",
693
+ defaultMin = 0,
694
+ defaultMax = 100,
695
+ defaultPrecision = 2,
696
+ status = "normal",
697
+ collapsed = false,
698
+ onCollapseChange,
699
+ size = "medium",
700
+ setpointReadOnly = false,
701
+ showStartStopButtons = false,
702
+ isRunning = false,
703
+ onStart,
704
+ onStop,
705
+ startButtonText = "Start",
706
+ stopButtonText = "Stop",
707
+ className = "",
708
+ ...props
709
+ }) => {
710
+ const [isExpanded, setIsExpanded] = (0, import_react3.useState)(!collapsed);
711
+ const toggleExpanded = () => {
712
+ const newExpanded = !isExpanded;
713
+ setIsExpanded(newExpanded);
714
+ onCollapseChange?.(!newExpanded);
715
+ };
716
+ const currentModeConfig = modeConfigs[selectedMode] || {};
717
+ const currentUnit = currentModeConfig.unit || defaultUnit;
718
+ const currentLabel = currentModeConfig.label || defaultLabel;
719
+ const currentMin = currentModeConfig.min ?? defaultMin;
720
+ const currentMax = currentModeConfig.max ?? defaultMax;
721
+ const currentPrecision = currentModeConfig.precision ?? defaultPrecision;
722
+ const hasSecondSetpoint = currentModeConfig.hasSecondSetpoint || false;
723
+ const getSizeStyles = () => {
724
+ switch (size) {
725
+ case "small":
726
+ return {
727
+ padding: "1rem",
728
+ titleSize: "1rem",
729
+ gap: "0.75rem"
730
+ };
731
+ case "large":
732
+ return {
733
+ padding: "1.5rem",
734
+ titleSize: "1.5rem",
735
+ gap: "1.25rem"
736
+ };
737
+ default:
738
+ return {
739
+ padding: "1.25rem",
740
+ titleSize: "1.125rem",
741
+ gap: "1rem"
742
+ };
743
+ }
744
+ };
745
+ const getStatusStyles = () => {
746
+ switch (status) {
747
+ case "alarm":
748
+ return {
749
+ borderColor: "var(--control-panel-alarm-border, #ef4444)",
750
+ backgroundColor: "var(--control-panel-alarm-bg, rgba(239, 68, 68, 0.1))",
751
+ titleColor: "var(--control-panel-alarm-text, #fecaca)",
752
+ boxShadow: "var(--control-panel-alarm-shadow, 0 0 20px rgba(239, 68, 68, 0.3))",
753
+ glowEffect: "var(--control-panel-alarm-glow, 0 0 30px rgba(239, 68, 68, 0.2))"
754
+ };
755
+ case "warning":
756
+ return {
757
+ borderColor: "var(--control-panel-warning-border, #f59e0b)",
758
+ backgroundColor: "var(--control-panel-warning-bg, rgba(245, 158, 11, 0.1))",
759
+ titleColor: "var(--control-panel-warning-text, #fed7aa)",
760
+ boxShadow: "var(--control-panel-warning-shadow, 0 0 20px rgba(245, 158, 11, 0.3))",
761
+ glowEffect: "var(--control-panel-warning-glow, 0 0 30px rgba(245, 158, 11, 0.2))"
762
+ };
763
+ case "disabled":
764
+ return {
765
+ borderColor: "var(--control-panel-disabled-border, #6b7280)",
766
+ backgroundColor: "var(--control-panel-disabled-bg, rgba(107, 114, 128, 0.1))",
767
+ titleColor: "var(--control-panel-disabled-text, #9ca3af)",
768
+ boxShadow: "var(--control-panel-disabled-shadow, 0 8px 32px rgba(0, 0, 0, 0.2))",
769
+ glowEffect: "var(--control-panel-disabled-glow, none)"
770
+ };
771
+ default:
772
+ return {
773
+ borderColor: "var(--control-panel-normal-border, #64748b)",
774
+ backgroundColor: "var(--control-panel-normal-bg, rgba(30, 41, 59, 0.9))",
775
+ titleColor: "var(--control-panel-normal-text, #f1f5f9)",
776
+ boxShadow: "var(--control-panel-normal-shadow, 0 8px 32px rgba(0, 0, 0, 0.3))",
777
+ glowEffect: "var(--control-panel-normal-glow, 0 0 20px rgba(59, 130, 246, 0.1))"
778
+ };
779
+ }
780
+ };
781
+ const sizeStyles = getSizeStyles();
782
+ const statusStyles = getStatusStyles();
783
+ const panelStyle = {
784
+ border: "2px solid",
785
+ borderRadius: "1.5rem",
786
+ backdropFilter: "blur(12px)",
787
+ transition: "all 400ms cubic-bezier(0.4, 0, 0.2, 1)",
788
+ position: "relative",
789
+ overflow: "hidden",
790
+ minWidth: "300px",
791
+ maxWidth: "400px",
792
+ minHeight: "400px",
793
+ width: "100%",
794
+ ...statusStyles,
795
+ boxShadow: isExpanded ? statusStyles.glowEffect : statusStyles.boxShadow
796
+ };
797
+ const headerStyle = {
798
+ display: "flex",
799
+ alignItems: "center",
800
+ justifyContent: "space-between",
801
+ padding: sizeStyles.padding,
802
+ cursor: onCollapseChange ? "pointer" : "default",
803
+ borderBottom: isExpanded ? `1px solid ${statusStyles.borderColor}40` : "none",
804
+ transition: "all 300ms ease"
805
+ };
806
+ const titleStyle = {
807
+ fontSize: sizeStyles.titleSize,
808
+ fontWeight: "bold",
809
+ color: statusStyles.titleColor,
810
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
811
+ letterSpacing: "0.05em",
812
+ margin: 0
813
+ };
814
+ const contentStyle = {
815
+ padding: isExpanded ? sizeStyles.padding : "0",
816
+ paddingTop: isExpanded ? sizeStyles.padding : "0",
817
+ maxHeight: isExpanded ? "500px" : "0",
818
+ opacity: isExpanded ? 1 : 0,
819
+ overflow: "hidden",
820
+ transition: "all 400ms cubic-bezier(0.4, 0, 0.2, 1)",
821
+ display: "flex",
822
+ flexDirection: "column",
823
+ gap: sizeStyles.gap,
824
+ justifyContent: "space-between",
825
+ flex: 1
826
+ };
827
+ const chevronStyle = {
828
+ width: "1.5rem",
829
+ height: "1.5rem",
830
+ color: statusStyles.titleColor,
831
+ transform: `rotate(${isExpanded ? "180deg" : "0deg"})`,
832
+ transition: "transform 300ms ease",
833
+ opacity: onCollapseChange ? 1 : 0.3
834
+ };
835
+ const statusBadgeStyle = {
836
+ position: "absolute",
837
+ top: "0.5rem",
838
+ right: "0.5rem",
839
+ width: "0.75rem",
840
+ height: "0.75rem",
841
+ borderRadius: "50%",
842
+ backgroundColor: status === "alarm" ? "#ef4444" : status === "warning" ? "#f59e0b" : status === "disabled" ? "#6b7280" : "#22c55e",
843
+ boxShadow: `0 0 10px ${status === "alarm" ? "#ef4444" : status === "warning" ? "#f59e0b" : status === "disabled" ? "#6b7280" : "#22c55e"}80`,
844
+ animation: status === "alarm" ? "pulse 2s infinite" : "none"
845
+ };
846
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: panelStyle, className: `control-panel glass-card ${className}`, ...props, children: [
847
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: statusBadgeStyle }),
848
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: headerStyle, onClick: onCollapseChange ? toggleExpanded : void 0, children: [
849
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { style: titleStyle, children: title }),
850
+ onCollapseChange && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { style: chevronStyle, fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })
851
+ ] }),
852
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: contentStyle, children: [
853
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
854
+ Selector,
855
+ {
856
+ value: selectedMode,
857
+ onChange: onModeChange,
858
+ options: modeOptions,
859
+ label: "Control Mode",
860
+ status,
861
+ size: size === "small" ? "small" : size === "large" ? "large" : "medium",
862
+ disabled: status === "disabled"
863
+ }
864
+ ) }),
865
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
866
+ ValueEntry,
867
+ {
868
+ value: setpointValue,
869
+ onChange: onSetpointChange,
870
+ label: currentLabel,
871
+ unit: currentUnit,
872
+ min: currentMin,
873
+ max: currentMax,
874
+ precision: currentPrecision,
875
+ status,
876
+ readOnly: setpointReadOnly || status === "disabled",
877
+ disabled: status === "disabled"
878
+ }
879
+ ) }),
880
+ hasSecondSetpoint && onSecondSetpointChange && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
881
+ ValueEntry,
882
+ {
883
+ value: secondSetpointValue || 0,
884
+ onChange: onSecondSetpointChange,
885
+ label: currentModeConfig.secondLabel || "Time",
886
+ unit: currentModeConfig.secondUnit || "sec",
887
+ min: currentModeConfig.secondMin ?? 0,
888
+ max: currentModeConfig.secondMax ?? 999,
889
+ precision: currentModeConfig.secondPrecision ?? 0,
890
+ status,
891
+ readOnly: setpointReadOnly || status === "disabled",
892
+ disabled: status === "disabled"
893
+ }
894
+ ) }),
895
+ showStartStopButtons && (onStart || onStop) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: "0.75rem" }, children: [
896
+ onStart && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
897
+ Button,
898
+ {
899
+ variant: "primary",
900
+ onClick: onStart,
901
+ disabled: isRunning || status === "disabled",
902
+ style: {
903
+ flex: 1,
904
+ fontSize: "0.875rem",
905
+ padding: "0.75rem 1rem",
906
+ backgroundColor: isRunning ? "#6b7280" : void 0
907
+ },
908
+ children: startButtonText
909
+ }
910
+ ),
911
+ onStop && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
912
+ Button,
913
+ {
914
+ variant: "secondary",
915
+ onClick: onStop,
916
+ disabled: !isRunning || status === "disabled",
917
+ style: {
918
+ flex: 1,
919
+ fontSize: "0.875rem",
920
+ padding: "0.75rem 1rem",
921
+ backgroundColor: !isRunning ? "#6b7280" : "#dc2626",
922
+ borderColor: !isRunning ? "#6b7280" : "#dc2626"
923
+ },
924
+ children: stopButtonText
925
+ }
926
+ )
927
+ ] }),
928
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
929
+ "div",
930
+ {
931
+ className: "control-panel-info",
932
+ style: {
933
+ display: "flex",
934
+ justifyContent: "space-between",
935
+ alignItems: "center",
936
+ padding: "0.5rem 0",
937
+ fontSize: "0.75rem",
938
+ color: "var(--control-panel-info-text, #9ca3af)",
939
+ borderTop: `1px solid ${statusStyles.borderColor}20`,
940
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
941
+ },
942
+ children: [
943
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
944
+ "Mode: ",
945
+ selectedMode.toUpperCase()
946
+ ] }),
947
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: "1rem" }, children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
949
+ "SP: ",
950
+ String(setpointValue),
951
+ currentUnit
952
+ ] }),
953
+ hasSecondSetpoint && secondSetpointValue && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
954
+ currentModeConfig.secondLabel,
955
+ ": ",
956
+ String(secondSetpointValue),
957
+ currentModeConfig.secondUnit
958
+ ] })
959
+ ] })
960
+ ]
961
+ }
962
+ )
963
+ ] })
964
+ ] });
965
+ };
966
+
967
+ // src/ThemeContext.tsx
968
+ var import_react4 = require("react");
969
+ var import_jsx_runtime6 = require("react/jsx-runtime");
970
+ var ThemeContext = (0, import_react4.createContext)(void 0);
971
+ var useTheme = () => {
972
+ const context = (0, import_react4.useContext)(ThemeContext);
973
+ if (!context) {
974
+ throw new Error("useTheme must be used within a ThemeProvider");
975
+ }
976
+ return context;
977
+ };
978
+ var ThemeProvider = ({ children }) => {
979
+ const [theme, setThemeState] = (0, import_react4.useState)("modern");
980
+ (0, import_react4.useEffect)(() => {
981
+ document.body.className = "";
982
+ document.body.classList.add(`theme-${theme}`);
983
+ document.documentElement.className = "";
984
+ document.documentElement.classList.add(`theme-${theme}`);
985
+ }, [theme]);
986
+ const toggleTheme = () => {
987
+ setTheme(theme === "modern" ? "hmi" : "modern");
988
+ };
989
+ const setTheme = (newTheme) => {
990
+ setThemeState(newTheme);
991
+ };
992
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ThemeContext.Provider, { value: { theme, toggleTheme, setTheme }, children });
993
+ };
994
+
995
+ // src/ThemeToggle.tsx
996
+ var import_jsx_runtime7 = require("react/jsx-runtime");
997
+ var ThemeToggle = ({
998
+ size = "medium",
999
+ position = "top-right",
1000
+ className = "",
1001
+ ...props
1002
+ }) => {
1003
+ const { theme, toggleTheme } = useTheme();
1004
+ const sizeStyles = {
1005
+ small: { padding: "0.5rem 1rem", fontSize: "0.75rem" },
1006
+ medium: { padding: "0.75rem 1.5rem", fontSize: "0.875rem" },
1007
+ large: { padding: "1rem 2rem", fontSize: "1rem" }
1008
+ };
1009
+ const positionStyles = {
1010
+ "top-left": { position: "fixed", top: "1rem", left: "1rem", zIndex: 1e3 },
1011
+ "top-right": { position: "fixed", top: "1rem", right: "1rem", zIndex: 1e3 },
1012
+ "bottom-left": { position: "fixed", bottom: "1rem", left: "1rem", zIndex: 1e3 },
1013
+ "bottom-right": { position: "fixed", bottom: "1rem", right: "1rem", zIndex: 1e3 },
1014
+ inline: { position: "relative" }
1015
+ };
1016
+ const baseStyle = {
1017
+ display: "flex",
1018
+ alignItems: "center",
1019
+ gap: "0.75rem",
1020
+ cursor: "pointer",
1021
+ borderRadius: "0.5rem",
1022
+ border: "2px solid",
1023
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
1024
+ fontWeight: 600,
1025
+ textTransform: "uppercase",
1026
+ letterSpacing: "0.05em",
1027
+ transition: "all 0.2s ease",
1028
+ userSelect: "none",
1029
+ ...sizeStyles[size],
1030
+ ...positionStyles[position]
1031
+ };
1032
+ const modernStyle = {
1033
+ ...baseStyle,
1034
+ background: "linear-gradient(135deg, #3b82f6 0%, #2563eb 50%, #1d4ed8 100%)",
1035
+ borderColor: "rgba(59, 130, 246, 0.4)",
1036
+ color: "#ffffff",
1037
+ boxShadow: "0 8px 32px rgba(59, 130, 246, 0.3)"
1038
+ };
1039
+ const hmiStyle = {
1040
+ ...baseStyle,
1041
+ backgroundColor: "#666666",
1042
+ borderColor: "#999999",
1043
+ color: "#ffffff",
1044
+ boxShadow: "none"
1045
+ };
1046
+ const currentStyle = theme === "modern" ? modernStyle : hmiStyle;
1047
+ const toggleStyle = {
1048
+ position: "relative",
1049
+ width: "3rem",
1050
+ height: "1.5rem",
1051
+ borderRadius: "0.75rem",
1052
+ transition: "all 0.2s ease",
1053
+ backgroundColor: theme === "modern" ? "#3b82f6" : "#808080",
1054
+ border: `2px solid ${theme === "modern" ? "#2563eb" : "#666666"}`
1055
+ };
1056
+ const thumbStyle = {
1057
+ position: "absolute",
1058
+ top: "0.125rem",
1059
+ left: theme === "modern" ? "0.125rem" : "1.375rem",
1060
+ width: "1rem",
1061
+ height: "1rem",
1062
+ borderRadius: "50%",
1063
+ backgroundColor: "#ffffff",
1064
+ transition: "all 0.2s ease",
1065
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)"
1066
+ };
1067
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: currentStyle, onClick: toggleTheme, className, ...props, children: [
1068
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: theme === "modern" ? "\u{1F3A8} Modern" : "\u{1F3ED} HMI" }),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: toggleStyle, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: thumbStyle }) }),
1070
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: "0.75rem", opacity: 0.8 }, children: theme === "modern" ? "Industrial Design" : "High Performance" })
1071
+ ] });
1072
+ };
1073
+ // Annotate the CommonJS export names for ESM import in node:
1074
+ 0 && (module.exports = {
1075
+ Button,
1076
+ ControlPanel,
1077
+ Selector,
1078
+ StatusIndicator,
1079
+ ThemeProvider,
1080
+ ThemeToggle,
1081
+ ValueEntry,
1082
+ useTheme
1083
+ });
1084
+ //# sourceMappingURL=index.cjs.map