tyrell-react 1.0.0-RC10

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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +410 -0
  3. package/dist/components/TyButton.d.ts +52 -0
  4. package/dist/components/TyButton.d.ts.map +1 -0
  5. package/dist/components/TyButton.js +76 -0
  6. package/dist/components/TyButton.js.map +1 -0
  7. package/dist/components/TyCalendar.d.ts +63 -0
  8. package/dist/components/TyCalendar.d.ts.map +1 -0
  9. package/dist/components/TyCalendar.js +128 -0
  10. package/dist/components/TyCalendar.js.map +1 -0
  11. package/dist/components/TyCalendarMonth.d.ts +32 -0
  12. package/dist/components/TyCalendarMonth.d.ts.map +1 -0
  13. package/dist/components/TyCalendarMonth.js +54 -0
  14. package/dist/components/TyCalendarMonth.js.map +1 -0
  15. package/dist/components/TyCalendarNavigation.d.ts +21 -0
  16. package/dist/components/TyCalendarNavigation.d.ts.map +1 -0
  17. package/dist/components/TyCalendarNavigation.js +50 -0
  18. package/dist/components/TyCalendarNavigation.js.map +1 -0
  19. package/dist/components/TyCheckbox.d.ts +39 -0
  20. package/dist/components/TyCheckbox.d.ts.map +1 -0
  21. package/dist/components/TyCheckbox.js +76 -0
  22. package/dist/components/TyCheckbox.js.map +1 -0
  23. package/dist/components/TyCopy.d.ts +21 -0
  24. package/dist/components/TyCopy.d.ts.map +1 -0
  25. package/dist/components/TyCopy.js +46 -0
  26. package/dist/components/TyCopy.js.map +1 -0
  27. package/dist/components/TyDatePicker.d.ts +45 -0
  28. package/dist/components/TyDatePicker.d.ts.map +1 -0
  29. package/dist/components/TyDatePicker.js +122 -0
  30. package/dist/components/TyDatePicker.js.map +1 -0
  31. package/dist/components/TyDropdown.d.ts +62 -0
  32. package/dist/components/TyDropdown.d.ts.map +1 -0
  33. package/dist/components/TyDropdown.js +124 -0
  34. package/dist/components/TyDropdown.js.map +1 -0
  35. package/dist/components/TyFileUpload.d.ts +31 -0
  36. package/dist/components/TyFileUpload.d.ts.map +1 -0
  37. package/dist/components/TyFileUpload.js +56 -0
  38. package/dist/components/TyFileUpload.js.map +1 -0
  39. package/dist/components/TyIcon.d.ts +17 -0
  40. package/dist/components/TyIcon.d.ts.map +1 -0
  41. package/dist/components/TyIcon.js +42 -0
  42. package/dist/components/TyIcon.js.map +1 -0
  43. package/dist/components/TyInput.d.ts +65 -0
  44. package/dist/components/TyInput.d.ts.map +1 -0
  45. package/dist/components/TyInput.js +134 -0
  46. package/dist/components/TyInput.js.map +1 -0
  47. package/dist/components/TyModal.d.ts +48 -0
  48. package/dist/components/TyModal.d.ts.map +1 -0
  49. package/dist/components/TyModal.js +120 -0
  50. package/dist/components/TyModal.js.map +1 -0
  51. package/dist/components/TyMultiselect.d.ts +57 -0
  52. package/dist/components/TyMultiselect.d.ts.map +1 -0
  53. package/dist/components/TyMultiselect.js +111 -0
  54. package/dist/components/TyMultiselect.js.map +1 -0
  55. package/dist/components/TyOption.d.ts +10 -0
  56. package/dist/components/TyOption.d.ts.map +1 -0
  57. package/dist/components/TyOption.js +29 -0
  58. package/dist/components/TyOption.js.map +1 -0
  59. package/dist/components/TyPopup.d.ts +24 -0
  60. package/dist/components/TyPopup.d.ts.map +1 -0
  61. package/dist/components/TyPopup.js +70 -0
  62. package/dist/components/TyPopup.js.map +1 -0
  63. package/dist/components/TyRadio.d.ts +20 -0
  64. package/dist/components/TyRadio.d.ts.map +1 -0
  65. package/dist/components/TyRadio.js +35 -0
  66. package/dist/components/TyRadio.js.map +1 -0
  67. package/dist/components/TyRadioGroup.d.ts +40 -0
  68. package/dist/components/TyRadioGroup.d.ts.map +1 -0
  69. package/dist/components/TyRadioGroup.js +61 -0
  70. package/dist/components/TyRadioGroup.js.map +1 -0
  71. package/dist/components/TyResizeObserver.d.ts +11 -0
  72. package/dist/components/TyResizeObserver.d.ts.map +1 -0
  73. package/dist/components/TyResizeObserver.js +28 -0
  74. package/dist/components/TyResizeObserver.js.map +1 -0
  75. package/dist/components/TyScrollContainer.d.ts +25 -0
  76. package/dist/components/TyScrollContainer.d.ts.map +1 -0
  77. package/dist/components/TyScrollContainer.js +61 -0
  78. package/dist/components/TyScrollContainer.js.map +1 -0
  79. package/dist/components/TyStep.d.ts +17 -0
  80. package/dist/components/TyStep.d.ts.map +1 -0
  81. package/dist/components/TyStep.js +35 -0
  82. package/dist/components/TyStep.js.map +1 -0
  83. package/dist/components/TySwitch.d.ts +35 -0
  84. package/dist/components/TySwitch.d.ts.map +1 -0
  85. package/dist/components/TySwitch.js +59 -0
  86. package/dist/components/TySwitch.js.map +1 -0
  87. package/dist/components/TyTab.d.ts +13 -0
  88. package/dist/components/TyTab.d.ts.map +1 -0
  89. package/dist/components/TyTab.js +34 -0
  90. package/dist/components/TyTab.js.map +1 -0
  91. package/dist/components/TyTabs.d.ts +23 -0
  92. package/dist/components/TyTabs.d.ts.map +1 -0
  93. package/dist/components/TyTabs.js +48 -0
  94. package/dist/components/TyTabs.js.map +1 -0
  95. package/dist/components/TyTag.d.ts +22 -0
  96. package/dist/components/TyTag.d.ts.map +1 -0
  97. package/dist/components/TyTag.js +51 -0
  98. package/dist/components/TyTag.js.map +1 -0
  99. package/dist/components/TyTextarea.d.ts +37 -0
  100. package/dist/components/TyTextarea.d.ts.map +1 -0
  101. package/dist/components/TyTextarea.js +116 -0
  102. package/dist/components/TyTextarea.js.map +1 -0
  103. package/dist/components/TyTooltip.d.ts +17 -0
  104. package/dist/components/TyTooltip.d.ts.map +1 -0
  105. package/dist/components/TyTooltip.js +41 -0
  106. package/dist/components/TyTooltip.js.map +1 -0
  107. package/dist/components/TyWizard.d.ts +26 -0
  108. package/dist/components/TyWizard.d.ts.map +1 -0
  109. package/dist/components/TyWizard.js +50 -0
  110. package/dist/components/TyWizard.js.map +1 -0
  111. package/dist/components/index.d.ts +112 -0
  112. package/dist/components/index.d.ts.map +1 -0
  113. package/dist/components/index.js +127 -0
  114. package/dist/components/index.js.map +1 -0
  115. package/dist/utils/react-version.d.ts +2 -0
  116. package/dist/utils/react-version.d.ts.map +1 -0
  117. package/dist/utils/react-version.js +8 -0
  118. package/dist/utils/react-version.js.map +1 -0
  119. package/dist/utils/use-boolean-prop.d.ts +36 -0
  120. package/dist/utils/use-boolean-prop.d.ts.map +1 -0
  121. package/dist/utils/use-boolean-prop.js +62 -0
  122. package/dist/utils/use-boolean-prop.js.map +1 -0
  123. package/dist/version.d.ts +3 -0
  124. package/dist/version.d.ts.map +1 -0
  125. package/dist/version.js +6 -0
  126. package/dist/version.js.map +1 -0
  127. package/package.json +47 -0
  128. package/src/components/EventConventionTest.tsx +155 -0
  129. package/src/components/TyButton.tsx +157 -0
  130. package/src/components/TyCalendar.tsx +247 -0
  131. package/src/components/TyCalendarMonth.tsx +108 -0
  132. package/src/components/TyCalendarNavigation.tsx +91 -0
  133. package/src/components/TyCheckbox.tsx +147 -0
  134. package/src/components/TyCopy.tsx +83 -0
  135. package/src/components/TyDatePicker.tsx +215 -0
  136. package/src/components/TyDropdown.tsx +240 -0
  137. package/src/components/TyFileUpload.tsx +108 -0
  138. package/src/components/TyIcon.tsx +71 -0
  139. package/src/components/TyInput.tsx +239 -0
  140. package/src/components/TyModal.tsx +195 -0
  141. package/src/components/TyMultiselect.tsx +208 -0
  142. package/src/components/TyOption.tsx +47 -0
  143. package/src/components/TyPopup.tsx +116 -0
  144. package/src/components/TyRadio.tsx +61 -0
  145. package/src/components/TyRadioGroup.tsx +125 -0
  146. package/src/components/TyResizeObserver.tsx +54 -0
  147. package/src/components/TyScrollContainer.tsx +102 -0
  148. package/src/components/TyStep.tsx +71 -0
  149. package/src/components/TySwitch.tsx +114 -0
  150. package/src/components/TyTab.tsx +65 -0
  151. package/src/components/TyTabs.tsx +93 -0
  152. package/src/components/TyTag.tsx +86 -0
  153. package/src/components/TyTextarea.tsx +181 -0
  154. package/src/components/TyTooltip.tsx +83 -0
  155. package/src/components/TyWizard.tsx +99 -0
  156. package/src/components/index.ts +279 -0
  157. package/src/utils/react-version.ts +8 -0
  158. package/src/utils/use-boolean-prop.ts +62 -0
  159. package/src/version.ts +6 -0
@@ -0,0 +1,65 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
3
+
4
+ // Type definitions for Ty Tab component
5
+ export interface TyTabProps extends React.HTMLAttributes<HTMLElement> {
6
+ /** Required unique identifier */
7
+ id: string;
8
+
9
+ /** Simple text label */
10
+ label?: string;
11
+
12
+ /** Whether the tab is disabled */
13
+ disabled?: boolean;
14
+
15
+ /** Tab content */
16
+ children?: React.ReactNode;
17
+ }
18
+
19
+ // React wrapper for ty-tab web component
20
+ export const TyTab = React.forwardRef<HTMLElement, TyTabProps>(
21
+ ({
22
+ children,
23
+ id,
24
+ label,
25
+ disabled,
26
+ ...props
27
+ }, ref) => {
28
+ const elementRef = useRef<HTMLElement>(null);
29
+
30
+ // Combine refs if needed
31
+ useEffect(() => {
32
+ if (ref && elementRef.current) {
33
+ if (typeof ref === 'function') {
34
+ ref(elementRef.current);
35
+ } else {
36
+ ref.current = elementRef.current;
37
+ }
38
+ }
39
+ }, [ref]);
40
+
41
+ // Convert React props to web component attributes
42
+ const webComponentProps: Record<string, any> = {
43
+ ...props,
44
+ ref: elementRef,
45
+ };
46
+
47
+ // Add required attribute
48
+ webComponentProps.id = id;
49
+
50
+ // Add boolean attributes
51
+ const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
52
+ if (isDisabled) webComponentProps.disabled = '';
53
+
54
+ // Add string attributes
55
+ if (label) webComponentProps.label = label;
56
+
57
+ return React.createElement(
58
+ 'ty-tab',
59
+ webComponentProps,
60
+ children
61
+ );
62
+ }
63
+ );
64
+
65
+ TyTab.displayName = 'TyTab';
@@ -0,0 +1,93 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ // Type definitions for Ty Tabs component
4
+ export interface TyTabsProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
5
+ /** Content area width (accepts px or %) */
6
+ width?: string;
7
+
8
+ /** Total container height including buttons */
9
+ height?: string;
10
+
11
+ /** ID of currently active tab */
12
+ active?: string;
13
+
14
+ /** Position of tab buttons */
15
+ placement?: 'top' | 'bottom';
16
+
17
+ /** Tab change event handler */
18
+ onChange?: (event: CustomEvent<TabChangeDetail>) => void;
19
+
20
+ /** Tabs content (TyTab components) */
21
+ children?: React.ReactNode;
22
+ }
23
+
24
+ export interface TabChangeDetail {
25
+ activeId: string;
26
+ activeIndex: number;
27
+ previousId: string | null;
28
+ previousIndex: number | null;
29
+ }
30
+
31
+ // React wrapper for ty-tabs web component
32
+ export const TyTabs = React.forwardRef<HTMLElement, TyTabsProps>(
33
+ ({
34
+ children,
35
+ width,
36
+ height,
37
+ active,
38
+ placement,
39
+ onChange,
40
+ ...props
41
+ }, ref) => {
42
+ const elementRef = useRef<HTMLElement>(null);
43
+
44
+ // Handle change events
45
+ useEffect(() => {
46
+ const element = elementRef.current;
47
+ if (!element) return;
48
+
49
+ const handleChange = (event: Event) => {
50
+ if (onChange) {
51
+ onChange(event as CustomEvent<TabChangeDetail>);
52
+ }
53
+ };
54
+
55
+ element.addEventListener('ty-tab-change', handleChange);
56
+
57
+ return () => {
58
+ element.removeEventListener('ty-tab-change', handleChange);
59
+ };
60
+ }, [onChange]);
61
+
62
+ // Combine refs if needed
63
+ useEffect(() => {
64
+ if (ref && elementRef.current) {
65
+ if (typeof ref === 'function') {
66
+ ref(elementRef.current);
67
+ } else {
68
+ ref.current = elementRef.current;
69
+ }
70
+ }
71
+ }, [ref]);
72
+
73
+ // Convert React props to web component attributes
74
+ const webComponentProps: Record<string, any> = {
75
+ ...props,
76
+ ref: elementRef,
77
+ };
78
+
79
+ // Add string attributes
80
+ if (width) webComponentProps.width = width;
81
+ if (height) webComponentProps.height = height;
82
+ if (active) webComponentProps.active = active;
83
+ if (placement) webComponentProps.placement = placement;
84
+
85
+ return React.createElement(
86
+ 'ty-tabs',
87
+ webComponentProps,
88
+ children
89
+ );
90
+ }
91
+ );
92
+
93
+ TyTabs.displayName = 'TyTabs';
@@ -0,0 +1,86 @@
1
+ import React, { useEffect, useRef, useCallback } from 'react';
2
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
3
+
4
+ // CSS custom properties that cascade into the shadow DOM for full color control
5
+ export interface TyTagCSSProperties extends React.CSSProperties {
6
+ '--tag-bg'?: string;
7
+ '--tag-color'?: string;
8
+ '--tag-border-color'?: string;
9
+ }
10
+
11
+ // Type definitions for Ty Tag component
12
+ export interface TyTagProps extends Omit<React.HTMLAttributes<HTMLElement>, 'style' | 'onClick'> {
13
+ flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
14
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
15
+ notPill?: boolean;
16
+ clickable?: boolean;
17
+ dismissible?: boolean;
18
+ disabled?: boolean;
19
+ selected?: boolean;
20
+ value?: string;
21
+ style?: TyTagCSSProperties;
22
+ // click is a composed CustomEvent from the web component — React's onClick picks it up
23
+ onClick?: (event: CustomEvent) => void;
24
+ onTagDismiss?: (event: CustomEvent) => void;
25
+ children?: React.ReactNode;
26
+ }
27
+
28
+ // React wrapper for ty-tag web component
29
+ export const TyTag = React.forwardRef<HTMLElement, TyTagProps>(
30
+ ({ children, onClick, onTagDismiss, notPill, clickable, dismissible, disabled, selected, ...props }, ref) => {
31
+ const elementRef = useRef<HTMLElement>(null);
32
+
33
+ const handleDismiss = useCallback((event: CustomEvent) => {
34
+ if (onTagDismiss) {
35
+ onTagDismiss(event);
36
+ }
37
+ }, [onTagDismiss]);
38
+
39
+ // dismiss is a custom event — React doesn't know about it, so we need a manual listener
40
+ useEffect(() => {
41
+ const element = elementRef.current;
42
+ if (!element || !onTagDismiss) return;
43
+
44
+ element.addEventListener('dismiss', handleDismiss as EventListener);
45
+ return () => {
46
+ element.removeEventListener('dismiss', handleDismiss as EventListener);
47
+ };
48
+ }, [handleDismiss, onTagDismiss]);
49
+
50
+ // Handle ref forwarding
51
+ useEffect(() => {
52
+ if (ref && elementRef.current) {
53
+ if (typeof ref === 'function') {
54
+ ref(elementRef.current);
55
+ } else {
56
+ ref.current = elementRef.current;
57
+ }
58
+ }
59
+ }, [ref]);
60
+
61
+ const isNotPill = useBooleanProperty(elementRef, 'notPill', notPill);
62
+ const isClickable = useBooleanProperty(elementRef, 'clickable', clickable);
63
+ const isDismissible = useBooleanProperty(elementRef, 'dismissible', dismissible);
64
+ const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
65
+ const isSelected = useBooleanProperty(elementRef, 'selected', selected);
66
+
67
+ return React.createElement(
68
+ 'ty-tag',
69
+ {
70
+ ...props,
71
+ // click is dispatched as composed CustomEvent by the web component — React's
72
+ // synthetic onClick already catches it, so we just pass it through as onClick
73
+ ...(onClick && { onClick }),
74
+ ...(isNotPill && { 'not-pill': "" }),
75
+ ...(isClickable && { clickable: "" }),
76
+ ...(isDismissible && { dismissible: "" }),
77
+ ...(isDisabled && { disabled: "" }),
78
+ ...(isSelected && { selected: "" }),
79
+ ref: elementRef,
80
+ },
81
+ children
82
+ );
83
+ }
84
+ );
85
+
86
+ TyTag.displayName = 'TyTag';
@@ -0,0 +1,181 @@
1
+ import React, { useEffect, useRef, useCallback } from 'react';
2
+ import { needsPropertyBridge } from '../utils/react-version';
3
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
4
+
5
+ // Event detail structure for ty-textarea events
6
+ export interface TyTextareaEventDetail {
7
+ value: string; // textarea value
8
+ originalEvent: Event; // original DOM event
9
+ }
10
+
11
+ // Type definitions for Ty Textarea component
12
+ export interface TyTextareaProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'onInput' | 'onFocus' | 'onBlur' | 'style'> {
13
+ style?: import('./TyInput').TyInputCSSProperties;
14
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
15
+ value?: string;
16
+ placeholder?: string;
17
+ label?: string;
18
+ error?: string;
19
+ disabled?: boolean;
20
+ required?: boolean;
21
+ name?: string; // Important for HTMX/form compatibility
22
+
23
+ // Textarea-specific props
24
+ rows?: string | number;
25
+ cols?: string | number;
26
+ resize?: 'none' | 'both' | 'horizontal' | 'vertical';
27
+ minHeight?: string; // e.g., '100px' - converts to min-height
28
+ maxHeight?: string; // e.g., '500px' - converts to max-height
29
+
30
+ // React event handlers - override with our custom types
31
+ /**
32
+ * Fires on every keystroke (React convention)
33
+ * Maps to native 'input' event from ty-textarea
34
+ */
35
+ onChange?: (event: CustomEvent<TyTextareaEventDetail>) => void;
36
+
37
+ /**
38
+ * Fires on blur if value changed (native DOM behavior)
39
+ * Maps to native 'change' event from ty-textarea
40
+ */
41
+ onChangeCommit?: (event: CustomEvent<TyTextareaEventDetail>) => void;
42
+
43
+ /** Standard focus event */
44
+ onFocus?: (event: FocusEvent) => void;
45
+
46
+ /** Standard blur event */
47
+ onBlur?: (event: FocusEvent) => void;
48
+ }
49
+
50
+ // One-time warning flag.
51
+ let _warnedOnInputProp = false;
52
+
53
+ // React wrapper for ty-textarea web component
54
+ export const TyTextarea = React.forwardRef<HTMLElement, TyTextareaProps>(
55
+ ({ onChange, onChangeCommit, onFocus, onBlur, disabled, required, minHeight, maxHeight, ...props }, ref) => {
56
+ const elementRef = useRef<HTMLElement>(null);
57
+
58
+ // Same `onInput` → `onChange` redirect as TyInput. React's synthetic-event
59
+ // system strips event.detail; users hitting e.detail.value will crash.
60
+ const onInputProp = (props as any).onInput as ((e: any) => void) | undefined;
61
+ if (onInputProp && !onChange) {
62
+ if (!_warnedOnInputProp) {
63
+ _warnedOnInputProp = true;
64
+ console.warn(
65
+ '[tyrell-react] <TyTextarea> received `onInput`. ' +
66
+ 'React strips event.detail; use `onChange` instead — it receives the raw CustomEvent. ' +
67
+ 'Forwarding for now, but please rename the prop.'
68
+ );
69
+ }
70
+ onChange = onInputProp;
71
+ }
72
+ delete (props as any).onInput;
73
+
74
+ // Map onChange to input event (React convention)
75
+ const handleInput = useCallback((event: CustomEvent<TyTextareaEventDetail>) => {
76
+ if (onChange) {
77
+ onChange(event);
78
+ }
79
+ }, [onChange]);
80
+
81
+ // Map onChangeCommit to change event (blur behavior)
82
+ const handleChangeCommit = useCallback((event: CustomEvent<TyTextareaEventDetail>) => {
83
+ if (onChangeCommit) {
84
+ onChangeCommit(event);
85
+ }
86
+ }, [onChangeCommit]);
87
+
88
+ const handleFocus = useCallback((event: FocusEvent) => {
89
+ if (onFocus) {
90
+ onFocus(event);
91
+ }
92
+ }, [onFocus]);
93
+
94
+ const handleBlur = useCallback((event: FocusEvent) => {
95
+ if (onBlur) {
96
+ onBlur(event);
97
+ }
98
+ }, [onBlur]);
99
+
100
+ useEffect(() => {
101
+ const element = elementRef.current;
102
+ if (!element) return;
103
+
104
+ // Listen for custom input/change events from ty-textarea
105
+ // Map onChange → input event (React convention)
106
+ if (onChange) {
107
+ element.addEventListener('input', handleInput as EventListener);
108
+ }
109
+
110
+ // Map onChangeCommit → change event (blur behavior)
111
+ if (onChangeCommit) {
112
+ element.addEventListener('change', handleChangeCommit as EventListener);
113
+ }
114
+
115
+ // Listen for standard focus/blur events
116
+ if (onFocus) {
117
+ element.addEventListener('focus', handleFocus as EventListener);
118
+ }
119
+
120
+ if (onBlur) {
121
+ element.addEventListener('blur', handleBlur as EventListener);
122
+ }
123
+
124
+ return () => {
125
+ if (onChange) {
126
+ element.removeEventListener('input', handleInput as EventListener);
127
+ }
128
+ if (onChangeCommit) {
129
+ element.removeEventListener('change', handleChangeCommit as EventListener);
130
+ }
131
+ if (onFocus) {
132
+ element.removeEventListener('focus', handleFocus as EventListener);
133
+ }
134
+ if (onBlur) {
135
+ element.removeEventListener('blur', handleBlur as EventListener);
136
+ }
137
+ };
138
+ }, [handleInput, handleChangeCommit, handleFocus, handleBlur, onChange, onChangeCommit, onFocus, onBlur]);
139
+
140
+ // Handle ref forwarding
141
+ useEffect(() => {
142
+ if (ref && elementRef.current) {
143
+ if (typeof ref === 'function') {
144
+ ref(elementRef.current);
145
+ } else {
146
+ ref.current = elementRef.current;
147
+ }
148
+ }
149
+ }, [ref]);
150
+
151
+ // Imperatively sync `value` to the underlying element's property.
152
+ // React 18 workaround: prop-to-property bridging is unreliable for empty
153
+ // strings on custom elements. React 19+ handles this natively.
154
+ useEffect(() => {
155
+ if (!needsPropertyBridge) return;
156
+ const element = elementRef.current as any;
157
+ if (!element) return;
158
+ const next = (props as any).value ?? '';
159
+ if (element.value !== next) {
160
+ element.value = next;
161
+ }
162
+ }, [(props as any).value]);
163
+
164
+ const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
165
+ const isRequired = useBooleanProperty(elementRef, 'required', required);
166
+
167
+ return React.createElement(
168
+ 'ty-textarea',
169
+ {
170
+ ...props,
171
+ ...(isDisabled && { disabled: "" }),
172
+ ...(isRequired && { required: "" }),
173
+ ...(minHeight && { 'min-height': minHeight }), // Convert camelCase to kebab-case
174
+ ...(maxHeight && { 'max-height': maxHeight }), // Convert camelCase to kebab-case
175
+ ref: elementRef,
176
+ }
177
+ );
178
+ }
179
+ );
180
+
181
+ TyTextarea.displayName = 'TyTextarea';
@@ -0,0 +1,83 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
3
+
4
+ // Type definitions for Ty Tooltip component
5
+ export interface TyTooltipProps extends React.HTMLAttributes<HTMLElement> {
6
+ /** Tooltip positioning relative to the parent element */
7
+ placement?: 'top' | 'bottom' | 'left' | 'right';
8
+
9
+ /** Distance in pixels from the anchor element (default: 8) */
10
+ offset?: number;
11
+
12
+ /** Delay in milliseconds before showing tooltip (default: 600) */
13
+ delay?: number;
14
+
15
+ /** Disable the tooltip */
16
+ disabled?: boolean;
17
+
18
+ /** Semantic styling variant */
19
+ flavor?: 'dark' | 'light' | 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'neutral';
20
+
21
+ /** Tooltip content */
22
+ children?: React.ReactNode;
23
+ }
24
+
25
+ // React wrapper for ty-tooltip web component
26
+ export const TyTooltip = React.forwardRef<HTMLElement, TyTooltipProps>(
27
+ ({
28
+ placement,
29
+ offset,
30
+ delay,
31
+ disabled,
32
+ flavor,
33
+ children,
34
+ ...props
35
+ }, ref) => {
36
+ const elementRef = useRef<HTMLElement>(null);
37
+
38
+ // Handle ref forwarding
39
+ useEffect(() => {
40
+ if (ref && elementRef.current) {
41
+ if (typeof ref === 'function') {
42
+ ref(elementRef.current);
43
+ } else {
44
+ ref.current = elementRef.current;
45
+ }
46
+ }
47
+ }, [ref]);
48
+
49
+ // Convert React props to web component attributes
50
+ const webComponentProps: Record<string, any> = {
51
+ ...props,
52
+ ref: elementRef,
53
+ };
54
+
55
+ // Add optional attributes only if they have values
56
+ if (placement) {
57
+ webComponentProps.placement = placement;
58
+ }
59
+
60
+ if (offset !== undefined) {
61
+ webComponentProps.offset = offset.toString();
62
+ }
63
+
64
+ if (delay !== undefined) {
65
+ webComponentProps.delay = delay.toString();
66
+ }
67
+
68
+ const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
69
+ if (isDisabled) webComponentProps.disabled = '';
70
+
71
+ if (flavor) {
72
+ webComponentProps.flavor = flavor;
73
+ }
74
+
75
+ return React.createElement(
76
+ 'ty-tooltip',
77
+ webComponentProps,
78
+ children
79
+ );
80
+ }
81
+ );
82
+
83
+ TyTooltip.displayName = 'TyTooltip';
@@ -0,0 +1,99 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ // Type definitions for Ty Wizard component
4
+ export interface TyWizardProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
5
+ /** Content area width (accepts px or %) */
6
+ width?: string;
7
+
8
+ /** Total container height including step indicators */
9
+ height?: string;
10
+
11
+ /** ID of currently active step */
12
+ active?: string;
13
+
14
+ /** Comma-separated IDs of completed steps */
15
+ completed?: string;
16
+
17
+ /** Step indicator layout */
18
+ orientation?: 'horizontal' | 'vertical';
19
+
20
+ /** Step change event handler */
21
+ onStepChange?: (event: CustomEvent<WizardStepChangeDetail>) => void;
22
+
23
+ /** Wizard content (TyStep components) */
24
+ children?: React.ReactNode;
25
+ }
26
+
27
+ export interface WizardStepChangeDetail {
28
+ activeId: string;
29
+ activeIndex: number;
30
+ previousId: string | null;
31
+ previousIndex: number | null;
32
+ direction: 'forward' | 'backward' | 'none';
33
+ }
34
+
35
+ // React wrapper for ty-wizard web component
36
+ export const TyWizard = React.forwardRef<HTMLElement, TyWizardProps>(
37
+ ({
38
+ children,
39
+ width,
40
+ height,
41
+ active,
42
+ completed,
43
+ orientation,
44
+ onStepChange,
45
+ ...props
46
+ }, ref) => {
47
+ const elementRef = useRef<HTMLElement>(null);
48
+
49
+ // Handle step change events
50
+ useEffect(() => {
51
+ const element = elementRef.current;
52
+ if (!element) return;
53
+
54
+ const handleStepChange = (event: Event) => {
55
+ if (onStepChange) {
56
+ onStepChange(event as CustomEvent<WizardStepChangeDetail>);
57
+ }
58
+ };
59
+
60
+ element.addEventListener('ty-wizard-step-change', handleStepChange);
61
+
62
+ return () => {
63
+ element.removeEventListener('ty-wizard-step-change', handleStepChange);
64
+ };
65
+ }, [onStepChange]);
66
+
67
+ // Combine refs if needed
68
+ useEffect(() => {
69
+ if (ref && elementRef.current) {
70
+ if (typeof ref === 'function') {
71
+ ref(elementRef.current);
72
+ } else {
73
+ ref.current = elementRef.current;
74
+ }
75
+ }
76
+ }, [ref]);
77
+
78
+ // Convert React props to web component attributes
79
+ const webComponentProps: Record<string, any> = {
80
+ ...props,
81
+ ref: elementRef,
82
+ };
83
+
84
+ // Add string attributes
85
+ if (width) webComponentProps.width = width;
86
+ if (height) webComponentProps.height = height;
87
+ if (active) webComponentProps.active = active;
88
+ if (completed) webComponentProps.completed = completed;
89
+ if (orientation) webComponentProps.orientation = orientation;
90
+
91
+ return React.createElement(
92
+ 'ty-wizard',
93
+ webComponentProps,
94
+ children
95
+ );
96
+ }
97
+ );
98
+
99
+ TyWizard.displayName = 'TyWizard';