@imperosoft/cris-webui-components 0.1.0-beta.1
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 +106 -0
- package/dist/index.d.mts +48 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +127 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +100 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @imperosoft/cris-webui-components
|
|
2
|
+
|
|
3
|
+
**CRIS** - Crestron React Impero Soft WebUI components library
|
|
4
|
+
|
|
5
|
+
Provides reusable UI components for Crestron touch panel interfaces.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @imperosoft/cris-webui-components
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Using Granular Access Token
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm config set //registry.npmjs.org/:_authToken=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
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Components
|
|
20
|
+
|
|
21
|
+
### CrisButton
|
|
22
|
+
|
|
23
|
+
A button component with Crestron join integration.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { CrisButton } from '@imperosoft/cris-webui-components';
|
|
27
|
+
|
|
28
|
+
// Basic button with join
|
|
29
|
+
<CrisButton
|
|
30
|
+
join={10}
|
|
31
|
+
text="Press Me"
|
|
32
|
+
className="bg-gray-600 p-4 rounded"
|
|
33
|
+
classActive="bg-red-600"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
// Button with separate feedback join
|
|
37
|
+
<CrisButton
|
|
38
|
+
join={10}
|
|
39
|
+
joinFeedback={11}
|
|
40
|
+
text="Off"
|
|
41
|
+
textSelected="On"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
// Button with visibility and enable joins
|
|
45
|
+
<CrisButton
|
|
46
|
+
join={10}
|
|
47
|
+
joinVisible={100}
|
|
48
|
+
joinEnable={101}
|
|
49
|
+
text="Conditional Button"
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
// Button with icon
|
|
53
|
+
<CrisButton
|
|
54
|
+
join={10}
|
|
55
|
+
icon={<MyIcon />}
|
|
56
|
+
iconPosition="left"
|
|
57
|
+
text="With Icon"
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
// Display-only button (no press action)
|
|
61
|
+
<CrisButton
|
|
62
|
+
joinFeedback={10}
|
|
63
|
+
suppressKeyClicks
|
|
64
|
+
text="Status Indicator"
|
|
65
|
+
/>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Props
|
|
69
|
+
|
|
70
|
+
| Prop | Type | Default | Description |
|
|
71
|
+
|------|------|---------|-------------|
|
|
72
|
+
| `join` | `number` | - | Digital join for press action |
|
|
73
|
+
| `joinFeedback` | `number` | `join` | Digital join for feedback state |
|
|
74
|
+
| `joinEnable` | `number` | - | Digital join to enable/disable button |
|
|
75
|
+
| `joinVisible` | `number` | - | Digital join to show/hide button |
|
|
76
|
+
| `text` | `string` | - | Button text |
|
|
77
|
+
| `textPressed` | `string` | - | Text while pressed (local feedback) |
|
|
78
|
+
| `textSelected` | `string` | - | Text when active (controller feedback) |
|
|
79
|
+
| `icon` | `ReactNode` | - | Icon element |
|
|
80
|
+
| `iconPosition` | `'left' \| 'right' \| 'top' \| 'bottom'` | `'top'` | Icon position |
|
|
81
|
+
| `showControlFeedback` | `boolean` | `true` | Show controller feedback styling |
|
|
82
|
+
| `showLocalFeedback` | `boolean` | `true` | Show local press styling |
|
|
83
|
+
| `suppressKeyClicks` | `boolean` | `false` | Disable press actions |
|
|
84
|
+
| `className` | `string` | - | Base CSS class |
|
|
85
|
+
| `classActive` | `string` | - | CSS class when active |
|
|
86
|
+
| `classPressed` | `string` | - | CSS class when pressed |
|
|
87
|
+
| `classDisabled` | `string` | - | CSS class when disabled |
|
|
88
|
+
| `onPress` | `() => void` | - | Custom press handler |
|
|
89
|
+
| `onRelease` | `() => void` | - | Custom release handler |
|
|
90
|
+
|
|
91
|
+
### CSS Classes
|
|
92
|
+
|
|
93
|
+
The component automatically adds these classes based on state:
|
|
94
|
+
- `cris-button` - Always present
|
|
95
|
+
- `active` - When controller feedback is true
|
|
96
|
+
- `pressed` - When locally pressed
|
|
97
|
+
- `disabled` - When disabled
|
|
98
|
+
|
|
99
|
+
## Peer Dependencies
|
|
100
|
+
|
|
101
|
+
- `react` >= 18.0.0
|
|
102
|
+
- `@imperosoft/cris-webui-core` >= 0.1.0
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface CrisButtonProps {
|
|
5
|
+
/** Digital join for press action */
|
|
6
|
+
join?: number;
|
|
7
|
+
/** Digital join for feedback (defaults to join) */
|
|
8
|
+
joinFeedback?: number;
|
|
9
|
+
/** Digital join for enable state */
|
|
10
|
+
joinEnable?: number;
|
|
11
|
+
/** Digital join for visibility */
|
|
12
|
+
joinVisible?: number;
|
|
13
|
+
/** Button text */
|
|
14
|
+
text?: string;
|
|
15
|
+
/** Text when pressed (local feedback) */
|
|
16
|
+
textPressed?: string;
|
|
17
|
+
/** Text when selected (controller feedback) */
|
|
18
|
+
textSelected?: string;
|
|
19
|
+
/** Icon element or class */
|
|
20
|
+
icon?: ReactNode;
|
|
21
|
+
/** Icon position relative to text */
|
|
22
|
+
iconPosition?: 'left' | 'right' | 'top' | 'bottom';
|
|
23
|
+
/** Show controller feedback styling */
|
|
24
|
+
showControlFeedback?: boolean;
|
|
25
|
+
/** Show local press feedback styling */
|
|
26
|
+
showLocalFeedback?: boolean;
|
|
27
|
+
/** Suppress click actions (display only) */
|
|
28
|
+
suppressKeyClicks?: boolean;
|
|
29
|
+
/** Smart object ID (for smarts instead of joins) */
|
|
30
|
+
smartId?: number;
|
|
31
|
+
/** Custom class names */
|
|
32
|
+
className?: string;
|
|
33
|
+
/** Class when active (controller feedback) */
|
|
34
|
+
classActive?: string;
|
|
35
|
+
/** Class when pressed (local feedback) */
|
|
36
|
+
classPressed?: string;
|
|
37
|
+
/** Class when disabled */
|
|
38
|
+
classDisabled?: string;
|
|
39
|
+
/** Children content */
|
|
40
|
+
children?: ReactNode;
|
|
41
|
+
/** Custom click handler (called on press) */
|
|
42
|
+
onPress?: () => void;
|
|
43
|
+
/** Custom release handler */
|
|
44
|
+
onRelease?: () => void;
|
|
45
|
+
}
|
|
46
|
+
declare function CrisButton({ join, joinFeedback, joinEnable, joinVisible, text, textPressed, textSelected, icon, iconPosition, showControlFeedback, showLocalFeedback, suppressKeyClicks, smartId, className, classActive, classPressed, classDisabled, children, onPress, onRelease, }: CrisButtonProps): react_jsx_runtime.JSX.Element | null;
|
|
47
|
+
|
|
48
|
+
export { CrisButton, type CrisButtonProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface CrisButtonProps {
|
|
5
|
+
/** Digital join for press action */
|
|
6
|
+
join?: number;
|
|
7
|
+
/** Digital join for feedback (defaults to join) */
|
|
8
|
+
joinFeedback?: number;
|
|
9
|
+
/** Digital join for enable state */
|
|
10
|
+
joinEnable?: number;
|
|
11
|
+
/** Digital join for visibility */
|
|
12
|
+
joinVisible?: number;
|
|
13
|
+
/** Button text */
|
|
14
|
+
text?: string;
|
|
15
|
+
/** Text when pressed (local feedback) */
|
|
16
|
+
textPressed?: string;
|
|
17
|
+
/** Text when selected (controller feedback) */
|
|
18
|
+
textSelected?: string;
|
|
19
|
+
/** Icon element or class */
|
|
20
|
+
icon?: ReactNode;
|
|
21
|
+
/** Icon position relative to text */
|
|
22
|
+
iconPosition?: 'left' | 'right' | 'top' | 'bottom';
|
|
23
|
+
/** Show controller feedback styling */
|
|
24
|
+
showControlFeedback?: boolean;
|
|
25
|
+
/** Show local press feedback styling */
|
|
26
|
+
showLocalFeedback?: boolean;
|
|
27
|
+
/** Suppress click actions (display only) */
|
|
28
|
+
suppressKeyClicks?: boolean;
|
|
29
|
+
/** Smart object ID (for smarts instead of joins) */
|
|
30
|
+
smartId?: number;
|
|
31
|
+
/** Custom class names */
|
|
32
|
+
className?: string;
|
|
33
|
+
/** Class when active (controller feedback) */
|
|
34
|
+
classActive?: string;
|
|
35
|
+
/** Class when pressed (local feedback) */
|
|
36
|
+
classPressed?: string;
|
|
37
|
+
/** Class when disabled */
|
|
38
|
+
classDisabled?: string;
|
|
39
|
+
/** Children content */
|
|
40
|
+
children?: ReactNode;
|
|
41
|
+
/** Custom click handler (called on press) */
|
|
42
|
+
onPress?: () => void;
|
|
43
|
+
/** Custom release handler */
|
|
44
|
+
onRelease?: () => void;
|
|
45
|
+
}
|
|
46
|
+
declare function CrisButton({ join, joinFeedback, joinEnable, joinVisible, text, textPressed, textSelected, icon, iconPosition, showControlFeedback, showLocalFeedback, suppressKeyClicks, smartId, className, classActive, classPressed, classDisabled, children, onPress, onRelease, }: CrisButtonProps): react_jsx_runtime.JSX.Element | null;
|
|
47
|
+
|
|
48
|
+
export { CrisButton, type CrisButtonProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
CrisButton: () => CrisButton
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/components/CrisButton.tsx
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
var import_cris_webui_core = require("@imperosoft/cris-webui-core");
|
|
30
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
31
|
+
function CrisButton({
|
|
32
|
+
join,
|
|
33
|
+
joinFeedback,
|
|
34
|
+
joinEnable,
|
|
35
|
+
joinVisible,
|
|
36
|
+
text,
|
|
37
|
+
textPressed,
|
|
38
|
+
textSelected,
|
|
39
|
+
icon,
|
|
40
|
+
iconPosition = "top",
|
|
41
|
+
showControlFeedback = true,
|
|
42
|
+
showLocalFeedback = true,
|
|
43
|
+
suppressKeyClicks = false,
|
|
44
|
+
smartId,
|
|
45
|
+
className = "",
|
|
46
|
+
classActive = "",
|
|
47
|
+
classPressed = "",
|
|
48
|
+
classDisabled = "",
|
|
49
|
+
children,
|
|
50
|
+
onPress,
|
|
51
|
+
onRelease
|
|
52
|
+
}) {
|
|
53
|
+
const [pressed, setPressed] = (0, import_react.useState)(false);
|
|
54
|
+
const pressedRef = (0, import_react.useRef)(false);
|
|
55
|
+
const feedbackJoin = joinFeedback ?? join;
|
|
56
|
+
const feedback = (0, import_cris_webui_core.useDigital)(feedbackJoin ?? 0);
|
|
57
|
+
const enabled = (0, import_cris_webui_core.useDigital)(joinEnable ?? 0);
|
|
58
|
+
const visible = (0, import_cris_webui_core.useDigital)(joinVisible ?? 0);
|
|
59
|
+
const dSet = (0, import_cris_webui_core.useJoinsStore)((state) => state.dSet);
|
|
60
|
+
const isEnabled = joinEnable == null ? true : enabled;
|
|
61
|
+
const isVisible = joinVisible == null ? true : visible;
|
|
62
|
+
const hasControlFeedback = showControlFeedback && feedbackJoin != null && feedback;
|
|
63
|
+
const hasPressedFeedback = showLocalFeedback && pressed && isEnabled;
|
|
64
|
+
let currentText = text ?? "";
|
|
65
|
+
if (hasPressedFeedback && textPressed != null) {
|
|
66
|
+
currentText = textPressed;
|
|
67
|
+
} else if (hasControlFeedback && textSelected != null) {
|
|
68
|
+
currentText = textSelected;
|
|
69
|
+
}
|
|
70
|
+
const handlePress = () => {
|
|
71
|
+
if (suppressKeyClicks) return;
|
|
72
|
+
if (pressedRef.current) return;
|
|
73
|
+
pressedRef.current = true;
|
|
74
|
+
setPressed(true);
|
|
75
|
+
if (!isEnabled) return;
|
|
76
|
+
onPress?.();
|
|
77
|
+
if (join != null && smartId == null) {
|
|
78
|
+
dSet(join, true);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const handleRelease = () => {
|
|
82
|
+
if (suppressKeyClicks) return;
|
|
83
|
+
if (!pressedRef.current) return;
|
|
84
|
+
pressedRef.current = false;
|
|
85
|
+
setPressed(false);
|
|
86
|
+
if (!isEnabled) return;
|
|
87
|
+
onRelease?.();
|
|
88
|
+
if (join != null && smartId == null) {
|
|
89
|
+
dSet(join, false);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
if (!isVisible) return null;
|
|
93
|
+
const classes = [
|
|
94
|
+
"cris-button",
|
|
95
|
+
className,
|
|
96
|
+
hasControlFeedback && classActive,
|
|
97
|
+
hasPressedFeedback && classPressed,
|
|
98
|
+
!isEnabled && classDisabled,
|
|
99
|
+
hasControlFeedback && "active",
|
|
100
|
+
hasPressedFeedback && "pressed",
|
|
101
|
+
!isEnabled && "disabled"
|
|
102
|
+
].filter(Boolean).join(" ");
|
|
103
|
+
const flexDirection = iconPosition === "top" ? "flex-col" : iconPosition === "bottom" ? "flex-col-reverse" : iconPosition === "left" ? "flex-row" : "flex-row-reverse";
|
|
104
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
105
|
+
"div",
|
|
106
|
+
{
|
|
107
|
+
className: `${classes} flex items-center justify-center ${flexDirection}`,
|
|
108
|
+
style: { cursor: suppressKeyClicks ? "default" : "pointer" },
|
|
109
|
+
onMouseDown: handlePress,
|
|
110
|
+
onMouseUp: handleRelease,
|
|
111
|
+
onMouseLeave: handleRelease,
|
|
112
|
+
onTouchStart: handlePress,
|
|
113
|
+
onTouchEnd: handleRelease,
|
|
114
|
+
onTouchCancel: handleRelease,
|
|
115
|
+
children: [
|
|
116
|
+
children,
|
|
117
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cris-button-icon", children: icon }),
|
|
118
|
+
currentText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "cris-button-text", children: currentText })
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
124
|
+
0 && (module.exports = {
|
|
125
|
+
CrisButton
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/CrisButton.tsx"],"sourcesContent":["/**\r\n * @imperosoft/cris-webui-components\r\n *\r\n * CRIS - Crestron React Impero Soft WebUI components library\r\n *\r\n * Provides reusable UI components for Crestron touch panel interfaces.\r\n */\r\n\r\n// Components\r\nexport { CrisButton } from './components/CrisButton';\r\nexport type { CrisButtonProps } from './components/CrisButton';\r\n","import { useState, useRef, ReactNode } from 'react';\r\nimport { useDigital, useJoinsStore } from '@imperosoft/cris-webui-core';\r\n\r\nexport interface CrisButtonProps {\r\n /** Digital join for press action */\r\n join?: number;\r\n /** Digital join for feedback (defaults to join) */\r\n joinFeedback?: number;\r\n /** Digital join for enable state */\r\n joinEnable?: number;\r\n /** Digital join for visibility */\r\n joinVisible?: number;\r\n\r\n /** Button text */\r\n text?: string;\r\n /** Text when pressed (local feedback) */\r\n textPressed?: string;\r\n /** Text when selected (controller feedback) */\r\n textSelected?: string;\r\n\r\n /** Icon element or class */\r\n icon?: ReactNode;\r\n /** Icon position relative to text */\r\n iconPosition?: 'left' | 'right' | 'top' | 'bottom';\r\n\r\n /** Show controller feedback styling */\r\n showControlFeedback?: boolean;\r\n /** Show local press feedback styling */\r\n showLocalFeedback?: boolean;\r\n /** Suppress click actions (display only) */\r\n suppressKeyClicks?: boolean;\r\n\r\n /** Smart object ID (for smarts instead of joins) */\r\n smartId?: number;\r\n\r\n /** Custom class names */\r\n className?: string;\r\n /** Class when active (controller feedback) */\r\n classActive?: string;\r\n /** Class when pressed (local feedback) */\r\n classPressed?: string;\r\n /** Class when disabled */\r\n classDisabled?: string;\r\n\r\n /** Children content */\r\n children?: ReactNode;\r\n\r\n /** Custom click handler (called on press) */\r\n onPress?: () => void;\r\n /** Custom release handler */\r\n onRelease?: () => void;\r\n}\r\n\r\nexport function CrisButton({\r\n join,\r\n joinFeedback,\r\n joinEnable,\r\n joinVisible,\r\n text,\r\n textPressed,\r\n textSelected,\r\n icon,\r\n iconPosition = 'top',\r\n showControlFeedback = true,\r\n showLocalFeedback = true,\r\n suppressKeyClicks = false,\r\n smartId,\r\n className = '',\r\n classActive = '',\r\n classPressed = '',\r\n classDisabled = '',\r\n children,\r\n onPress,\r\n onRelease,\r\n}: CrisButtonProps) {\r\n const [pressed, setPressed] = useState(false);\r\n const pressedRef = useRef(false);\r\n\r\n // Get join values reactively\r\n const feedbackJoin = joinFeedback ?? join;\r\n const feedback = useDigital(feedbackJoin ?? 0);\r\n const enabled = useDigital(joinEnable ?? 0);\r\n const visible = useDigital(joinVisible ?? 0);\r\n\r\n // Get action method\r\n const dSet = useJoinsStore((state) => state.dSet);\r\n\r\n // Determine if button is enabled\r\n const isEnabled = joinEnable == null ? true : enabled;\r\n\r\n // Determine if button is visible\r\n const isVisible = joinVisible == null ? true : visible;\r\n\r\n // Determine current feedback state\r\n const hasControlFeedback = showControlFeedback && feedbackJoin != null && feedback;\r\n const hasPressedFeedback = showLocalFeedback && pressed && isEnabled;\r\n\r\n // Determine current text\r\n let currentText = text ?? '';\r\n if (hasPressedFeedback && textPressed != null) {\r\n currentText = textPressed;\r\n } else if (hasControlFeedback && textSelected != null) {\r\n currentText = textSelected;\r\n }\r\n\r\n // Event handlers\r\n const handlePress = () => {\r\n if (suppressKeyClicks) return;\r\n if (pressedRef.current) return;\r\n\r\n pressedRef.current = true;\r\n setPressed(true);\r\n\r\n if (!isEnabled) return;\r\n\r\n // Custom handler\r\n onPress?.();\r\n\r\n // Send to controller\r\n if (join != null && smartId == null) {\r\n dSet(join, true);\r\n }\r\n // TODO: Add smartId support when smarts are implemented in core\r\n };\r\n\r\n const handleRelease = () => {\r\n if (suppressKeyClicks) return;\r\n if (!pressedRef.current) return;\r\n\r\n pressedRef.current = false;\r\n setPressed(false);\r\n\r\n if (!isEnabled) return;\r\n\r\n // Custom handler\r\n onRelease?.();\r\n\r\n // Send to controller\r\n if (join != null && smartId == null) {\r\n dSet(join, false);\r\n }\r\n // TODO: Add smartId support when smarts are implemented in core\r\n };\r\n\r\n // Don't render if not visible\r\n if (!isVisible) return null;\r\n\r\n // Build class names\r\n const classes = [\r\n 'cris-button',\r\n className,\r\n hasControlFeedback && classActive,\r\n hasPressedFeedback && classPressed,\r\n !isEnabled && classDisabled,\r\n hasControlFeedback && 'active',\r\n hasPressedFeedback && 'pressed',\r\n !isEnabled && 'disabled',\r\n ]\r\n .filter(Boolean)\r\n .join(' ');\r\n\r\n // Flex direction based on icon position\r\n const flexDirection =\r\n iconPosition === 'top'\r\n ? 'flex-col'\r\n : iconPosition === 'bottom'\r\n ? 'flex-col-reverse'\r\n : iconPosition === 'left'\r\n ? 'flex-row'\r\n : 'flex-row-reverse';\r\n\r\n return (\r\n <div\r\n className={`${classes} flex items-center justify-center ${flexDirection}`}\r\n style={{ cursor: suppressKeyClicks ? 'default' : 'pointer' }}\r\n onMouseDown={handlePress}\r\n onMouseUp={handleRelease}\r\n onMouseLeave={handleRelease}\r\n onTouchStart={handlePress}\r\n onTouchEnd={handleRelease}\r\n onTouchCancel={handleRelease}\r\n >\r\n {children}\r\n {icon && <span className=\"cris-button-icon\">{icon}</span>}\r\n {currentText && <span className=\"cris-button-text\">{currentText}</span>}\r\n </div>\r\n );\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA4C;AAC5C,6BAA0C;AA2KtC;AAvHG,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB;AAAA,EACA,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,iBAAa,qBAAO,KAAK;AAG/B,QAAM,eAAe,gBAAgB;AACrC,QAAM,eAAW,mCAAW,gBAAgB,CAAC;AAC7C,QAAM,cAAU,mCAAW,cAAc,CAAC;AAC1C,QAAM,cAAU,mCAAW,eAAe,CAAC;AAG3C,QAAM,WAAO,sCAAc,CAAC,UAAU,MAAM,IAAI;AAGhD,QAAM,YAAY,cAAc,OAAO,OAAO;AAG9C,QAAM,YAAY,eAAe,OAAO,OAAO;AAG/C,QAAM,qBAAqB,uBAAuB,gBAAgB,QAAQ;AAC1E,QAAM,qBAAqB,qBAAqB,WAAW;AAG3D,MAAI,cAAc,QAAQ;AAC1B,MAAI,sBAAsB,eAAe,MAAM;AAC7C,kBAAc;AAAA,EAChB,WAAW,sBAAsB,gBAAgB,MAAM;AACrD,kBAAc;AAAA,EAChB;AAGA,QAAM,cAAc,MAAM;AACxB,QAAI,kBAAmB;AACvB,QAAI,WAAW,QAAS;AAExB,eAAW,UAAU;AACrB,eAAW,IAAI;AAEf,QAAI,CAAC,UAAW;AAGhB,cAAU;AAGV,QAAI,QAAQ,QAAQ,WAAW,MAAM;AACnC,WAAK,MAAM,IAAI;AAAA,IACjB;AAAA,EAEF;AAEA,QAAM,gBAAgB,MAAM;AAC1B,QAAI,kBAAmB;AACvB,QAAI,CAAC,WAAW,QAAS;AAEzB,eAAW,UAAU;AACrB,eAAW,KAAK;AAEhB,QAAI,CAAC,UAAW;AAGhB,gBAAY;AAGZ,QAAI,QAAQ,QAAQ,WAAW,MAAM;AACnC,WAAK,MAAM,KAAK;AAAA,IAClB;AAAA,EAEF;AAGA,MAAI,CAAC,UAAW,QAAO;AAGvB,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB,sBAAsB;AAAA,IACtB,CAAC,aAAa;AAAA,IACd,sBAAsB;AAAA,IACtB,sBAAsB;AAAA,IACtB,CAAC,aAAa;AAAA,EAChB,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAGX,QAAM,gBACJ,iBAAiB,QACb,aACA,iBAAiB,WACf,qBACA,iBAAiB,SACf,aACA;AAEV,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,OAAO,qCAAqC,aAAa;AAAA,MACvE,OAAO,EAAE,QAAQ,oBAAoB,YAAY,UAAU;AAAA,MAC3D,aAAa;AAAA,MACb,WAAW;AAAA,MACX,cAAc;AAAA,MACd,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,eAAe;AAAA,MAEd;AAAA;AAAA,QACA,QAAQ,4CAAC,UAAK,WAAU,oBAAoB,gBAAK;AAAA,QACjD,eAAe,4CAAC,UAAK,WAAU,oBAAoB,uBAAY;AAAA;AAAA;AAAA,EAClE;AAEJ;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/components/CrisButton.tsx
|
|
2
|
+
import { useState, useRef } from "react";
|
|
3
|
+
import { useDigital, useJoinsStore } from "@imperosoft/cris-webui-core";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
function CrisButton({
|
|
6
|
+
join,
|
|
7
|
+
joinFeedback,
|
|
8
|
+
joinEnable,
|
|
9
|
+
joinVisible,
|
|
10
|
+
text,
|
|
11
|
+
textPressed,
|
|
12
|
+
textSelected,
|
|
13
|
+
icon,
|
|
14
|
+
iconPosition = "top",
|
|
15
|
+
showControlFeedback = true,
|
|
16
|
+
showLocalFeedback = true,
|
|
17
|
+
suppressKeyClicks = false,
|
|
18
|
+
smartId,
|
|
19
|
+
className = "",
|
|
20
|
+
classActive = "",
|
|
21
|
+
classPressed = "",
|
|
22
|
+
classDisabled = "",
|
|
23
|
+
children,
|
|
24
|
+
onPress,
|
|
25
|
+
onRelease
|
|
26
|
+
}) {
|
|
27
|
+
const [pressed, setPressed] = useState(false);
|
|
28
|
+
const pressedRef = useRef(false);
|
|
29
|
+
const feedbackJoin = joinFeedback ?? join;
|
|
30
|
+
const feedback = useDigital(feedbackJoin ?? 0);
|
|
31
|
+
const enabled = useDigital(joinEnable ?? 0);
|
|
32
|
+
const visible = useDigital(joinVisible ?? 0);
|
|
33
|
+
const dSet = useJoinsStore((state) => state.dSet);
|
|
34
|
+
const isEnabled = joinEnable == null ? true : enabled;
|
|
35
|
+
const isVisible = joinVisible == null ? true : visible;
|
|
36
|
+
const hasControlFeedback = showControlFeedback && feedbackJoin != null && feedback;
|
|
37
|
+
const hasPressedFeedback = showLocalFeedback && pressed && isEnabled;
|
|
38
|
+
let currentText = text ?? "";
|
|
39
|
+
if (hasPressedFeedback && textPressed != null) {
|
|
40
|
+
currentText = textPressed;
|
|
41
|
+
} else if (hasControlFeedback && textSelected != null) {
|
|
42
|
+
currentText = textSelected;
|
|
43
|
+
}
|
|
44
|
+
const handlePress = () => {
|
|
45
|
+
if (suppressKeyClicks) return;
|
|
46
|
+
if (pressedRef.current) return;
|
|
47
|
+
pressedRef.current = true;
|
|
48
|
+
setPressed(true);
|
|
49
|
+
if (!isEnabled) return;
|
|
50
|
+
onPress?.();
|
|
51
|
+
if (join != null && smartId == null) {
|
|
52
|
+
dSet(join, true);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const handleRelease = () => {
|
|
56
|
+
if (suppressKeyClicks) return;
|
|
57
|
+
if (!pressedRef.current) return;
|
|
58
|
+
pressedRef.current = false;
|
|
59
|
+
setPressed(false);
|
|
60
|
+
if (!isEnabled) return;
|
|
61
|
+
onRelease?.();
|
|
62
|
+
if (join != null && smartId == null) {
|
|
63
|
+
dSet(join, false);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
if (!isVisible) return null;
|
|
67
|
+
const classes = [
|
|
68
|
+
"cris-button",
|
|
69
|
+
className,
|
|
70
|
+
hasControlFeedback && classActive,
|
|
71
|
+
hasPressedFeedback && classPressed,
|
|
72
|
+
!isEnabled && classDisabled,
|
|
73
|
+
hasControlFeedback && "active",
|
|
74
|
+
hasPressedFeedback && "pressed",
|
|
75
|
+
!isEnabled && "disabled"
|
|
76
|
+
].filter(Boolean).join(" ");
|
|
77
|
+
const flexDirection = iconPosition === "top" ? "flex-col" : iconPosition === "bottom" ? "flex-col-reverse" : iconPosition === "left" ? "flex-row" : "flex-row-reverse";
|
|
78
|
+
return /* @__PURE__ */ jsxs(
|
|
79
|
+
"div",
|
|
80
|
+
{
|
|
81
|
+
className: `${classes} flex items-center justify-center ${flexDirection}`,
|
|
82
|
+
style: { cursor: suppressKeyClicks ? "default" : "pointer" },
|
|
83
|
+
onMouseDown: handlePress,
|
|
84
|
+
onMouseUp: handleRelease,
|
|
85
|
+
onMouseLeave: handleRelease,
|
|
86
|
+
onTouchStart: handlePress,
|
|
87
|
+
onTouchEnd: handleRelease,
|
|
88
|
+
onTouchCancel: handleRelease,
|
|
89
|
+
children: [
|
|
90
|
+
children,
|
|
91
|
+
icon && /* @__PURE__ */ jsx("span", { className: "cris-button-icon", children: icon }),
|
|
92
|
+
currentText && /* @__PURE__ */ jsx("span", { className: "cris-button-text", children: currentText })
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
CrisButton
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/CrisButton.tsx"],"sourcesContent":["import { useState, useRef, ReactNode } from 'react';\r\nimport { useDigital, useJoinsStore } from '@imperosoft/cris-webui-core';\r\n\r\nexport interface CrisButtonProps {\r\n /** Digital join for press action */\r\n join?: number;\r\n /** Digital join for feedback (defaults to join) */\r\n joinFeedback?: number;\r\n /** Digital join for enable state */\r\n joinEnable?: number;\r\n /** Digital join for visibility */\r\n joinVisible?: number;\r\n\r\n /** Button text */\r\n text?: string;\r\n /** Text when pressed (local feedback) */\r\n textPressed?: string;\r\n /** Text when selected (controller feedback) */\r\n textSelected?: string;\r\n\r\n /** Icon element or class */\r\n icon?: ReactNode;\r\n /** Icon position relative to text */\r\n iconPosition?: 'left' | 'right' | 'top' | 'bottom';\r\n\r\n /** Show controller feedback styling */\r\n showControlFeedback?: boolean;\r\n /** Show local press feedback styling */\r\n showLocalFeedback?: boolean;\r\n /** Suppress click actions (display only) */\r\n suppressKeyClicks?: boolean;\r\n\r\n /** Smart object ID (for smarts instead of joins) */\r\n smartId?: number;\r\n\r\n /** Custom class names */\r\n className?: string;\r\n /** Class when active (controller feedback) */\r\n classActive?: string;\r\n /** Class when pressed (local feedback) */\r\n classPressed?: string;\r\n /** Class when disabled */\r\n classDisabled?: string;\r\n\r\n /** Children content */\r\n children?: ReactNode;\r\n\r\n /** Custom click handler (called on press) */\r\n onPress?: () => void;\r\n /** Custom release handler */\r\n onRelease?: () => void;\r\n}\r\n\r\nexport function CrisButton({\r\n join,\r\n joinFeedback,\r\n joinEnable,\r\n joinVisible,\r\n text,\r\n textPressed,\r\n textSelected,\r\n icon,\r\n iconPosition = 'top',\r\n showControlFeedback = true,\r\n showLocalFeedback = true,\r\n suppressKeyClicks = false,\r\n smartId,\r\n className = '',\r\n classActive = '',\r\n classPressed = '',\r\n classDisabled = '',\r\n children,\r\n onPress,\r\n onRelease,\r\n}: CrisButtonProps) {\r\n const [pressed, setPressed] = useState(false);\r\n const pressedRef = useRef(false);\r\n\r\n // Get join values reactively\r\n const feedbackJoin = joinFeedback ?? join;\r\n const feedback = useDigital(feedbackJoin ?? 0);\r\n const enabled = useDigital(joinEnable ?? 0);\r\n const visible = useDigital(joinVisible ?? 0);\r\n\r\n // Get action method\r\n const dSet = useJoinsStore((state) => state.dSet);\r\n\r\n // Determine if button is enabled\r\n const isEnabled = joinEnable == null ? true : enabled;\r\n\r\n // Determine if button is visible\r\n const isVisible = joinVisible == null ? true : visible;\r\n\r\n // Determine current feedback state\r\n const hasControlFeedback = showControlFeedback && feedbackJoin != null && feedback;\r\n const hasPressedFeedback = showLocalFeedback && pressed && isEnabled;\r\n\r\n // Determine current text\r\n let currentText = text ?? '';\r\n if (hasPressedFeedback && textPressed != null) {\r\n currentText = textPressed;\r\n } else if (hasControlFeedback && textSelected != null) {\r\n currentText = textSelected;\r\n }\r\n\r\n // Event handlers\r\n const handlePress = () => {\r\n if (suppressKeyClicks) return;\r\n if (pressedRef.current) return;\r\n\r\n pressedRef.current = true;\r\n setPressed(true);\r\n\r\n if (!isEnabled) return;\r\n\r\n // Custom handler\r\n onPress?.();\r\n\r\n // Send to controller\r\n if (join != null && smartId == null) {\r\n dSet(join, true);\r\n }\r\n // TODO: Add smartId support when smarts are implemented in core\r\n };\r\n\r\n const handleRelease = () => {\r\n if (suppressKeyClicks) return;\r\n if (!pressedRef.current) return;\r\n\r\n pressedRef.current = false;\r\n setPressed(false);\r\n\r\n if (!isEnabled) return;\r\n\r\n // Custom handler\r\n onRelease?.();\r\n\r\n // Send to controller\r\n if (join != null && smartId == null) {\r\n dSet(join, false);\r\n }\r\n // TODO: Add smartId support when smarts are implemented in core\r\n };\r\n\r\n // Don't render if not visible\r\n if (!isVisible) return null;\r\n\r\n // Build class names\r\n const classes = [\r\n 'cris-button',\r\n className,\r\n hasControlFeedback && classActive,\r\n hasPressedFeedback && classPressed,\r\n !isEnabled && classDisabled,\r\n hasControlFeedback && 'active',\r\n hasPressedFeedback && 'pressed',\r\n !isEnabled && 'disabled',\r\n ]\r\n .filter(Boolean)\r\n .join(' ');\r\n\r\n // Flex direction based on icon position\r\n const flexDirection =\r\n iconPosition === 'top'\r\n ? 'flex-col'\r\n : iconPosition === 'bottom'\r\n ? 'flex-col-reverse'\r\n : iconPosition === 'left'\r\n ? 'flex-row'\r\n : 'flex-row-reverse';\r\n\r\n return (\r\n <div\r\n className={`${classes} flex items-center justify-center ${flexDirection}`}\r\n style={{ cursor: suppressKeyClicks ? 'default' : 'pointer' }}\r\n onMouseDown={handlePress}\r\n onMouseUp={handleRelease}\r\n onMouseLeave={handleRelease}\r\n onTouchStart={handlePress}\r\n onTouchEnd={handleRelease}\r\n onTouchCancel={handleRelease}\r\n >\r\n {children}\r\n {icon && <span className=\"cris-button-icon\">{icon}</span>}\r\n {currentText && <span className=\"cris-button-text\">{currentText}</span>}\r\n </div>\r\n );\r\n}\r\n"],"mappings":";AAAA,SAAS,UAAU,cAAyB;AAC5C,SAAS,YAAY,qBAAqB;AA2KtC,SAWW,KAXX;AAvHG,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB;AAAA,EACA,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,aAAa,OAAO,KAAK;AAG/B,QAAM,eAAe,gBAAgB;AACrC,QAAM,WAAW,WAAW,gBAAgB,CAAC;AAC7C,QAAM,UAAU,WAAW,cAAc,CAAC;AAC1C,QAAM,UAAU,WAAW,eAAe,CAAC;AAG3C,QAAM,OAAO,cAAc,CAAC,UAAU,MAAM,IAAI;AAGhD,QAAM,YAAY,cAAc,OAAO,OAAO;AAG9C,QAAM,YAAY,eAAe,OAAO,OAAO;AAG/C,QAAM,qBAAqB,uBAAuB,gBAAgB,QAAQ;AAC1E,QAAM,qBAAqB,qBAAqB,WAAW;AAG3D,MAAI,cAAc,QAAQ;AAC1B,MAAI,sBAAsB,eAAe,MAAM;AAC7C,kBAAc;AAAA,EAChB,WAAW,sBAAsB,gBAAgB,MAAM;AACrD,kBAAc;AAAA,EAChB;AAGA,QAAM,cAAc,MAAM;AACxB,QAAI,kBAAmB;AACvB,QAAI,WAAW,QAAS;AAExB,eAAW,UAAU;AACrB,eAAW,IAAI;AAEf,QAAI,CAAC,UAAW;AAGhB,cAAU;AAGV,QAAI,QAAQ,QAAQ,WAAW,MAAM;AACnC,WAAK,MAAM,IAAI;AAAA,IACjB;AAAA,EAEF;AAEA,QAAM,gBAAgB,MAAM;AAC1B,QAAI,kBAAmB;AACvB,QAAI,CAAC,WAAW,QAAS;AAEzB,eAAW,UAAU;AACrB,eAAW,KAAK;AAEhB,QAAI,CAAC,UAAW;AAGhB,gBAAY;AAGZ,QAAI,QAAQ,QAAQ,WAAW,MAAM;AACnC,WAAK,MAAM,KAAK;AAAA,IAClB;AAAA,EAEF;AAGA,MAAI,CAAC,UAAW,QAAO;AAGvB,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB,sBAAsB;AAAA,IACtB,CAAC,aAAa;AAAA,IACd,sBAAsB;AAAA,IACtB,sBAAsB;AAAA,IACtB,CAAC,aAAa;AAAA,EAChB,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAGX,QAAM,gBACJ,iBAAiB,QACb,aACA,iBAAiB,WACf,qBACA,iBAAiB,SACf,aACA;AAEV,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,OAAO,qCAAqC,aAAa;AAAA,MACvE,OAAO,EAAE,QAAQ,oBAAoB,YAAY,UAAU;AAAA,MAC3D,aAAa;AAAA,MACb,WAAW;AAAA,MACX,cAAc;AAAA,MACd,cAAc;AAAA,MACd,YAAY;AAAA,MACZ,eAAe;AAAA,MAEd;AAAA;AAAA,QACA,QAAQ,oBAAC,UAAK,WAAU,oBAAoB,gBAAK;AAAA,QACjD,eAAe,oBAAC,UAAK,WAAU,oBAAoB,uBAAY;AAAA;AAAA;AAAA,EAClE;AAEJ;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imperosoft/cris-webui-components",
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
|
+
"description": "CRIS - Crestron React Impero Soft WebUI components library",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"lint": "eslint src/",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"crestron",
|
|
26
|
+
"react",
|
|
27
|
+
"webui",
|
|
28
|
+
"components",
|
|
29
|
+
"imperosoft"
|
|
30
|
+
],
|
|
31
|
+
"author": "Impero Soft <alex.bandol@imperosoft.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/imperosoft/cris-webui-components.git"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=18.0.0",
|
|
39
|
+
"@imperosoft/cris-webui-core": ">=0.1.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@imperosoft/cris-webui-core": "^0.1.0-beta.5",
|
|
43
|
+
"@types/react": "^18.2.0",
|
|
44
|
+
"react": "^18.2.0",
|
|
45
|
+
"tsup": "^8.0.0",
|
|
46
|
+
"typescript": "^5.3.0"
|
|
47
|
+
}
|
|
48
|
+
}
|