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,116 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
3
+
4
+ // Type definitions for Ty Popup component
5
+ export interface TyPopupProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onClose'> {
6
+ /** Preferred placement of the popup relative to anchor parent: "top" | "bottom" | "left" | "right" */
7
+ placement?: 'top' | 'bottom' | 'left' | 'right';
8
+
9
+ /** Distance offset from the anchor in pixels (default: 8) */
10
+ offset?: number;
11
+
12
+ /** Disable automatic click trigger - requires manual open/close via ref methods */
13
+ manual?: boolean;
14
+
15
+ /** Disable automatic close on outside click and ESC key */
16
+ disableClose?: boolean;
17
+
18
+ /** Fired when the popup opens (after the open animation starts) */
19
+ onOpen?: (event: CustomEvent) => void;
20
+
21
+ /** Fired when the popup closes */
22
+ onClose?: (event: CustomEvent) => void;
23
+
24
+ /** Popup content - popup should be a child of the anchor element */
25
+ children?: React.ReactNode;
26
+ }
27
+
28
+ // Programmatic API for popup control
29
+ export interface TyPopupElement extends HTMLElement {
30
+ openPopup(): void;
31
+ closePopup(): void;
32
+ togglePopup(): void;
33
+ }
34
+
35
+ // React wrapper for ty-popup web component
36
+ export const TyPopup = React.forwardRef<TyPopupElement, TyPopupProps>(
37
+ ({
38
+ placement,
39
+ offset,
40
+ manual,
41
+ disableClose,
42
+ onOpen,
43
+ onClose,
44
+ children,
45
+ ...props
46
+ }, ref) => {
47
+ const elementRef = useRef<TyPopupElement>(null);
48
+
49
+ // Handle ref forwarding
50
+ useEffect(() => {
51
+ if (ref && elementRef.current) {
52
+ if (typeof ref === 'function') {
53
+ ref(elementRef.current);
54
+ } else {
55
+ ref.current = elementRef.current;
56
+ }
57
+ }
58
+ }, [ref]);
59
+
60
+ // Listen for popup open/close events.
61
+ // Guard with `event.target === element` so bubbled open/close events
62
+ // from popup-like descendants (ty-dropdown, ty-multiselect, ty-date-picker
63
+ // when slotted inside this popup) don't fire the consumer's onOpen/onClose.
64
+ // See TyModal.tsx for the same pattern + rationale.
65
+ useEffect(() => {
66
+ const element = elementRef.current;
67
+ if (!element) return;
68
+
69
+ const handleOpen = (event: Event) => {
70
+ if (event.target !== element) return;
71
+ if (onOpen) onOpen(event as CustomEvent);
72
+ };
73
+ const handleClose = (event: Event) => {
74
+ if (event.target !== element) return;
75
+ if (onClose) onClose(event as CustomEvent);
76
+ };
77
+
78
+ if (onOpen) element.addEventListener('open', handleOpen);
79
+ if (onClose) element.addEventListener('close', handleClose);
80
+
81
+ return () => {
82
+ if (onOpen) element.removeEventListener('open', handleOpen);
83
+ if (onClose) element.removeEventListener('close', handleClose);
84
+ };
85
+ }, [onOpen, onClose]);
86
+
87
+ // Convert React props to web component attributes
88
+ const webComponentProps: Record<string, any> = {
89
+ ...props,
90
+ ref: elementRef,
91
+ };
92
+
93
+ // Add optional attributes only if they have values
94
+ if (placement) {
95
+ webComponentProps.placement = placement;
96
+ }
97
+
98
+ if (offset !== undefined) {
99
+ webComponentProps.offset = offset.toString();
100
+ }
101
+
102
+ const isManual = useBooleanProperty(elementRef, 'manual', manual);
103
+ const isDisableClose = useBooleanProperty(elementRef, 'disableClose', disableClose);
104
+
105
+ if (isManual) webComponentProps.manual = '';
106
+ if (isDisableClose) webComponentProps['disable-close'] = '';
107
+
108
+ return React.createElement(
109
+ 'ty-popup',
110
+ webComponentProps,
111
+ children
112
+ );
113
+ }
114
+ );
115
+
116
+ TyPopup.displayName = 'TyPopup';
@@ -0,0 +1,61 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
3
+
4
+ export interface TyRadioProps extends React.HTMLAttributes<HTMLElement> {
5
+ /** Form field value (selected by parent ty-radio-group when matches its `value`) */
6
+ value?: string;
7
+
8
+ /**
9
+ * Selected state. Usually managed by the parent `<TyRadioGroup>` based on its
10
+ * own `value`; set explicitly only when using `ty-radio` outside a group.
11
+ */
12
+ checked?: boolean;
13
+
14
+ /** Disable this individual radio */
15
+ disabled?: boolean;
16
+
17
+ /** Radio size — typically inherited from the parent group */
18
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
19
+
20
+ /** Semantic styling variant — typically inherited from the parent group */
21
+ flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
22
+
23
+ /** Label content (wrap in a `<label>` for click delegation, see ty-radio docs) */
24
+ children?: React.ReactNode;
25
+ }
26
+
27
+ export const TyRadio = React.forwardRef<HTMLElement, TyRadioProps>(
28
+ ({ children, value, checked, disabled, size, flavor, ...props }, ref) => {
29
+ const elementRef = useRef<HTMLElement>(null);
30
+
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
+ // Imperative property sync for boolean props (see use-boolean-prop.ts).
42
+ const isChecked = useBooleanProperty(elementRef, 'checked', checked);
43
+ const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
44
+
45
+ const webComponentProps: Record<string, any> = {
46
+ ...props,
47
+ ref: elementRef,
48
+ };
49
+
50
+ if (isChecked) webComponentProps.checked = '';
51
+ if (isDisabled) webComponentProps.disabled = '';
52
+
53
+ if (value !== undefined) webComponentProps.value = value;
54
+ if (size) webComponentProps.size = size;
55
+ if (flavor) webComponentProps.flavor = flavor;
56
+
57
+ return React.createElement('ty-radio', webComponentProps, children);
58
+ }
59
+ );
60
+
61
+ TyRadio.displayName = 'TyRadio';
@@ -0,0 +1,125 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
3
+
4
+ export interface TyRadioGroupProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'onInput'> {
5
+ /** Currently selected value (matches one child `<TyRadio value="...">`) */
6
+ value?: string;
7
+
8
+ /** Form field name */
9
+ name?: string;
10
+
11
+ /** Group label rendered above the radios */
12
+ label?: string;
13
+
14
+ /** Disable the entire group */
15
+ disabled?: boolean;
16
+
17
+ /** Required field — renders required-icon next to the label */
18
+ required?: boolean;
19
+
20
+ /** Error message rendered below the group */
21
+ error?: string;
22
+
23
+ /** Layout direction for radio children */
24
+ orientation?: 'vertical' | 'horizontal';
25
+
26
+ /** Group size — propagates to all `<TyRadio>` children */
27
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
28
+
29
+ /** Group flavor — propagates to all `<TyRadio>` children */
30
+ flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
31
+
32
+ /**
33
+ * Fires when selection changes (React convention)
34
+ * Maps to native 'input' event from ty-radio-group
35
+ */
36
+ onChange?: (event: CustomEvent<TyRadioGroupEventDetail>) => void;
37
+
38
+ /**
39
+ * Fires on blur if value changed (native DOM behavior)
40
+ * Maps to native 'change' event from ty-radio-group
41
+ */
42
+ onChangeCommit?: (event: CustomEvent<TyRadioGroupEventDetail>) => void;
43
+
44
+ /** `<TyRadio>` children */
45
+ children?: React.ReactNode;
46
+ }
47
+
48
+ export interface TyRadioGroupEventDetail {
49
+ value: string;
50
+ formValue: string;
51
+ originalEvent: Event;
52
+ }
53
+
54
+ export const TyRadioGroup = React.forwardRef<HTMLElement, TyRadioGroupProps>(
55
+ ({
56
+ children,
57
+ value,
58
+ name,
59
+ label,
60
+ disabled,
61
+ required,
62
+ error,
63
+ orientation,
64
+ size,
65
+ flavor,
66
+ onChange,
67
+ onChangeCommit,
68
+ ...props
69
+ }, ref) => {
70
+ const elementRef = useRef<HTMLElement>(null);
71
+
72
+ useEffect(() => {
73
+ const element = elementRef.current;
74
+ if (!element) return;
75
+
76
+ const handleInput = (event: Event) => {
77
+ if (onChange) onChange(event as CustomEvent<TyRadioGroupEventDetail>);
78
+ };
79
+ const handleChangeCommit = (event: Event) => {
80
+ if (onChangeCommit) onChangeCommit(event as CustomEvent<TyRadioGroupEventDetail>);
81
+ };
82
+
83
+ element.addEventListener('input', handleInput);
84
+ element.addEventListener('change', handleChangeCommit);
85
+
86
+ return () => {
87
+ element.removeEventListener('input', handleInput);
88
+ element.removeEventListener('change', handleChangeCommit);
89
+ };
90
+ }, [onChange, onChangeCommit]);
91
+
92
+ useEffect(() => {
93
+ if (ref && elementRef.current) {
94
+ if (typeof ref === 'function') {
95
+ ref(elementRef.current);
96
+ } else {
97
+ ref.current = elementRef.current;
98
+ }
99
+ }
100
+ }, [ref]);
101
+
102
+ const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
103
+ const isRequired = useBooleanProperty(elementRef, 'required', required);
104
+
105
+ const webComponentProps: Record<string, any> = {
106
+ ...props,
107
+ ref: elementRef,
108
+ };
109
+
110
+ if (isDisabled) webComponentProps.disabled = '';
111
+ if (isRequired) webComponentProps.required = '';
112
+
113
+ if (value !== undefined) webComponentProps.value = value;
114
+ if (name) webComponentProps.name = name;
115
+ if (label) webComponentProps.label = label;
116
+ if (error) webComponentProps.error = error;
117
+ if (orientation) webComponentProps.orientation = orientation;
118
+ if (size) webComponentProps.size = size;
119
+ if (flavor) webComponentProps.flavor = flavor;
120
+
121
+ return React.createElement('ty-radio-group', webComponentProps, children);
122
+ }
123
+ );
124
+
125
+ TyRadioGroup.displayName = 'TyRadioGroup';
@@ -0,0 +1,54 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ // Type definitions for Ty ResizeObserver component
4
+ export interface TyResizeObserverProps extends React.HTMLAttributes<HTMLElement> {
5
+ /** Required unique identifier for size registry */
6
+ id: string;
7
+
8
+ /** Debounce in milliseconds (default: 0 = no debounce) */
9
+ debounce?: number;
10
+
11
+ /** Content to observe */
12
+ children?: React.ReactNode;
13
+ }
14
+
15
+ // React wrapper for ty-resize-observer web component
16
+ export const TyResizeObserver = React.forwardRef<HTMLElement, TyResizeObserverProps>(
17
+ ({
18
+ children,
19
+ id,
20
+ debounce,
21
+ ...props
22
+ }, ref) => {
23
+ const elementRef = useRef<HTMLElement>(null);
24
+
25
+ // Combine refs if needed
26
+ useEffect(() => {
27
+ if (ref && elementRef.current) {
28
+ if (typeof ref === 'function') {
29
+ ref(elementRef.current);
30
+ } else {
31
+ ref.current = elementRef.current;
32
+ }
33
+ }
34
+ }, [ref]);
35
+
36
+ // Convert React props to web component attributes
37
+ const webComponentProps: Record<string, any> = {
38
+ ...props,
39
+ ref: elementRef,
40
+ id,
41
+ };
42
+
43
+ // Add number attributes
44
+ if (debounce !== undefined) webComponentProps.debounce = debounce;
45
+
46
+ return React.createElement(
47
+ 'ty-resize-observer',
48
+ webComponentProps,
49
+ children
50
+ );
51
+ }
52
+ );
53
+
54
+ TyResizeObserver.displayName = 'TyResizeObserver';
@@ -0,0 +1,102 @@
1
+ import React, { useEffect, useRef, useImperativeHandle } from 'react';
2
+ import { needsPropertyBridge } from '../utils/react-version';
3
+ import { useBooleanProperty, coerceBool } from '../utils/use-boolean-prop';
4
+
5
+ // Type definitions for Ty ScrollContainer component
6
+ export interface TyScrollContainerProps extends React.HTMLAttributes<HTMLElement> {
7
+ /** Maximum height of the scroll container */
8
+ maxHeight?: string;
9
+
10
+ /** Enable/disable scroll shadows (default: true) */
11
+ shadow?: boolean;
12
+
13
+ /** Hide native scrollbar */
14
+ hideScrollbar?: boolean;
15
+
16
+ /** Content to scroll */
17
+ children?: React.ReactNode;
18
+ }
19
+
20
+ // Ref interface for imperative methods
21
+ export interface TyScrollContainerRef {
22
+ /** Force update shadows (useful after dynamic content changes) */
23
+ updateShadows: () => void;
24
+ /** Scroll to top */
25
+ scrollToTop: (smooth?: boolean) => void;
26
+ /** Scroll to bottom */
27
+ scrollToBottom: (smooth?: boolean) => void;
28
+ /** Get the underlying scroll element */
29
+ scrollElement: HTMLElement | null;
30
+ /** Get the native element */
31
+ element: HTMLElement | null;
32
+ }
33
+
34
+ // React wrapper for ty-scroll-container web component
35
+ export const TyScrollContainer = React.forwardRef<TyScrollContainerRef, TyScrollContainerProps>(
36
+ ({
37
+ children,
38
+ maxHeight,
39
+ shadow,
40
+ hideScrollbar,
41
+ ...props
42
+ }, ref) => {
43
+ const elementRef = useRef<HTMLElement>(null);
44
+
45
+ // Expose imperative methods via ref
46
+ useImperativeHandle(ref, () => ({
47
+ updateShadows: () => {
48
+ const el = elementRef.current as any;
49
+ el?.updateShadows?.();
50
+ },
51
+ scrollToTop: (smooth = true) => {
52
+ const el = elementRef.current as any;
53
+ el?.scrollToTop?.(smooth);
54
+ },
55
+ scrollToBottom: (smooth = true) => {
56
+ const el = elementRef.current as any;
57
+ el?.scrollToBottom?.(smooth);
58
+ },
59
+ get scrollElement() {
60
+ const el = elementRef.current as any;
61
+ return el?.scrollElement ?? null;
62
+ },
63
+ get element() {
64
+ return elementRef.current;
65
+ }
66
+ }), []);
67
+
68
+ // shadow defaults to true; only the explicit-false case matters at the
69
+ // attribute level. Bridge it imperatively so flipping back to true
70
+ // propagates on React 18.
71
+ useEffect(() => {
72
+ if (!needsPropertyBridge) return;
73
+ if (shadow === undefined) return;
74
+ const el = elementRef.current as any;
75
+ if (!el) return;
76
+ const next = coerceBool(shadow);
77
+ if (Boolean(el.shadow) !== next) el.shadow = next;
78
+ }, [shadow]);
79
+ const isHideScrollbar = useBooleanProperty(elementRef, 'hideScrollbar', hideScrollbar);
80
+
81
+ // Convert React props to web component attributes
82
+ const webComponentProps: Record<string, any> = {
83
+ ...props,
84
+ ref: elementRef,
85
+ };
86
+
87
+ // Add string attributes
88
+ if (maxHeight) webComponentProps['max-height'] = maxHeight;
89
+
90
+ // Add boolean attributes
91
+ if (shadow !== undefined && !coerceBool(shadow)) webComponentProps.shadow = 'false';
92
+ if (isHideScrollbar) webComponentProps['hide-scrollbar'] = '';
93
+
94
+ return React.createElement(
95
+ 'ty-scroll-container',
96
+ webComponentProps,
97
+ children
98
+ );
99
+ }
100
+ );
101
+
102
+ TyScrollContainer.displayName = 'TyScrollContainer';
@@ -0,0 +1,71 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ // Type definitions for Ty Step component
4
+ export interface TyStepProps extends React.HTMLAttributes<HTMLElement> {
5
+ /** Required unique identifier for the step */
6
+ id: string;
7
+
8
+ /** Main step title displayed in indicator */
9
+ label?: string;
10
+
11
+ /** Optional subtitle/description */
12
+ description?: string;
13
+
14
+ /** Whether the step is disabled */
15
+ disabled?: boolean;
16
+
17
+ /** User-controlled status override */
18
+ status?: 'completed' | 'active' | 'pending' | 'error';
19
+
20
+ /** Step content */
21
+ children?: React.ReactNode;
22
+ }
23
+
24
+ // React wrapper for ty-step web component
25
+ export const TyStep = React.forwardRef<HTMLElement, TyStepProps>(
26
+ ({
27
+ children,
28
+ id,
29
+ label,
30
+ description,
31
+ disabled,
32
+ status,
33
+ ...props
34
+ }, ref) => {
35
+ const elementRef = useRef<HTMLElement>(null);
36
+
37
+ // Combine refs if needed
38
+ useEffect(() => {
39
+ if (ref && elementRef.current) {
40
+ if (typeof ref === 'function') {
41
+ ref(elementRef.current);
42
+ } else {
43
+ ref.current = elementRef.current;
44
+ }
45
+ }
46
+ }, [ref]);
47
+
48
+ // Convert React props to web component attributes
49
+ const webComponentProps: Record<string, any> = {
50
+ ...props,
51
+ ref: elementRef,
52
+ id,
53
+ };
54
+
55
+ // Add string attributes
56
+ if (label) webComponentProps.label = label;
57
+ if (description) webComponentProps.description = description;
58
+ if (status) webComponentProps.status = status;
59
+
60
+ // Add boolean attributes
61
+ if (disabled) webComponentProps.disabled = true;
62
+
63
+ return React.createElement(
64
+ 'ty-step',
65
+ webComponentProps,
66
+ children
67
+ );
68
+ }
69
+ );
70
+
71
+ TyStep.displayName = 'TyStep';
@@ -0,0 +1,114 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { useBooleanProperty } from '../utils/use-boolean-prop';
3
+
4
+ export interface TySwitchProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'onInput'> {
5
+ /** Checked (on) state */
6
+ checked?: boolean;
7
+
8
+ /** Form field value when checked */
9
+ value?: string;
10
+
11
+ /** Form field name */
12
+ name?: string;
13
+
14
+ /** Disable the switch */
15
+ disabled?: boolean;
16
+
17
+ /** Required field */
18
+ required?: boolean;
19
+
20
+ /** Switch size */
21
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
22
+
23
+ /** Semantic styling variant */
24
+ flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
25
+
26
+ /**
27
+ * Fires when switch state changes (React convention)
28
+ * Maps to native 'input' event from ty-switch
29
+ */
30
+ onChange?: (event: CustomEvent<TySwitchEventDetail>) => void;
31
+
32
+ /**
33
+ * Fires on blur if value changed (native DOM behavior)
34
+ * Maps to native 'change' event from ty-switch
35
+ */
36
+ onChangeCommit?: (event: CustomEvent<TySwitchEventDetail>) => void;
37
+ }
38
+
39
+ export interface TySwitchEventDetail {
40
+ value: boolean;
41
+ checked: boolean;
42
+ formValue: string | null;
43
+ originalEvent: Event;
44
+ }
45
+
46
+ export const TySwitch = React.forwardRef<HTMLElement, TySwitchProps>(
47
+ ({
48
+ checked,
49
+ value,
50
+ name,
51
+ disabled,
52
+ required,
53
+ size,
54
+ flavor,
55
+ onChange,
56
+ onChangeCommit,
57
+ ...props
58
+ }, ref) => {
59
+ const elementRef = useRef<HTMLElement>(null);
60
+
61
+ useEffect(() => {
62
+ const element = elementRef.current;
63
+ if (!element) return;
64
+
65
+ const handleInput = (event: Event) => {
66
+ if (onChange) onChange(event as CustomEvent<TySwitchEventDetail>);
67
+ };
68
+ const handleChangeCommit = (event: Event) => {
69
+ if (onChangeCommit) onChangeCommit(event as CustomEvent<TySwitchEventDetail>);
70
+ };
71
+
72
+ element.addEventListener('input', handleInput);
73
+ element.addEventListener('change', handleChangeCommit);
74
+
75
+ return () => {
76
+ element.removeEventListener('input', handleInput);
77
+ element.removeEventListener('change', handleChangeCommit);
78
+ };
79
+ }, [onChange, onChangeCommit]);
80
+
81
+ useEffect(() => {
82
+ if (ref && elementRef.current) {
83
+ if (typeof ref === 'function') {
84
+ ref(elementRef.current);
85
+ } else {
86
+ ref.current = elementRef.current;
87
+ }
88
+ }
89
+ }, [ref]);
90
+
91
+ // Imperative property sync for boolean props (see use-boolean-prop.ts).
92
+ const isChecked = useBooleanProperty(elementRef, 'checked', checked);
93
+ const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
94
+ const isRequired = useBooleanProperty(elementRef, 'required', required);
95
+
96
+ const webComponentProps: Record<string, any> = {
97
+ ...props,
98
+ ref: elementRef,
99
+ };
100
+
101
+ if (isChecked) webComponentProps.checked = '';
102
+ if (isDisabled) webComponentProps.disabled = '';
103
+ if (isRequired) webComponentProps.required = '';
104
+
105
+ if (value) webComponentProps.value = value;
106
+ if (name) webComponentProps.name = name;
107
+ if (size) webComponentProps.size = size;
108
+ if (flavor) webComponentProps.flavor = flavor;
109
+
110
+ return React.createElement('ty-switch', webComponentProps);
111
+ }
112
+ );
113
+
114
+ TySwitch.displayName = 'TySwitch';