@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 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
@@ -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 };
@@ -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
+ }