tyrell-react 1.0.0-RC6

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 (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +410 -0
  3. package/dist/components/TyButton.d.ts +50 -0
  4. package/dist/components/TyButton.d.ts.map +1 -0
  5. package/dist/components/TyButton.js +68 -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 +127 -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 +83 -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 +42 -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 +119 -0
  30. package/dist/components/TyDatePicker.js.map +1 -0
  31. package/dist/components/TyDropdown.d.ts +56 -0
  32. package/dist/components/TyDropdown.d.ts.map +1 -0
  33. package/dist/components/TyDropdown.js +110 -0
  34. package/dist/components/TyDropdown.js.map +1 -0
  35. package/dist/components/TyIcon.d.ts +17 -0
  36. package/dist/components/TyIcon.d.ts.map +1 -0
  37. package/dist/components/TyIcon.js +41 -0
  38. package/dist/components/TyIcon.js.map +1 -0
  39. package/dist/components/TyInput.d.ts +65 -0
  40. package/dist/components/TyInput.d.ts.map +1 -0
  41. package/dist/components/TyInput.js +127 -0
  42. package/dist/components/TyInput.js.map +1 -0
  43. package/dist/components/TyModal.d.ts +29 -0
  44. package/dist/components/TyModal.d.ts.map +1 -0
  45. package/dist/components/TyModal.js +74 -0
  46. package/dist/components/TyModal.js.map +1 -0
  47. package/dist/components/TyMultiselect.d.ts +51 -0
  48. package/dist/components/TyMultiselect.d.ts.map +1 -0
  49. package/dist/components/TyMultiselect.js +107 -0
  50. package/dist/components/TyMultiselect.js.map +1 -0
  51. package/dist/components/TyOption.d.ts +10 -0
  52. package/dist/components/TyOption.d.ts.map +1 -0
  53. package/dist/components/TyOption.js +25 -0
  54. package/dist/components/TyOption.js.map +1 -0
  55. package/dist/components/TyPopup.d.ts +24 -0
  56. package/dist/components/TyPopup.d.ts.map +1 -0
  57. package/dist/components/TyPopup.js +61 -0
  58. package/dist/components/TyPopup.js.map +1 -0
  59. package/dist/components/TyRadio.d.ts +20 -0
  60. package/dist/components/TyRadio.d.ts.map +1 -0
  61. package/dist/components/TyRadio.js +46 -0
  62. package/dist/components/TyRadio.js.map +1 -0
  63. package/dist/components/TyRadioGroup.d.ts +40 -0
  64. package/dist/components/TyRadioGroup.d.ts.map +1 -0
  65. package/dist/components/TyRadioGroup.js +58 -0
  66. package/dist/components/TyRadioGroup.js.map +1 -0
  67. package/dist/components/TyResizeObserver.d.ts +11 -0
  68. package/dist/components/TyResizeObserver.d.ts.map +1 -0
  69. package/dist/components/TyResizeObserver.js +28 -0
  70. package/dist/components/TyResizeObserver.js.map +1 -0
  71. package/dist/components/TyScrollContainer.d.ts +25 -0
  72. package/dist/components/TyScrollContainer.d.ts.map +1 -0
  73. package/dist/components/TyScrollContainer.js +43 -0
  74. package/dist/components/TyScrollContainer.js.map +1 -0
  75. package/dist/components/TyStep.d.ts +17 -0
  76. package/dist/components/TyStep.d.ts.map +1 -0
  77. package/dist/components/TyStep.js +35 -0
  78. package/dist/components/TyStep.js.map +1 -0
  79. package/dist/components/TySwitch.d.ts +35 -0
  80. package/dist/components/TySwitch.d.ts.map +1 -0
  81. package/dist/components/TySwitch.js +69 -0
  82. package/dist/components/TySwitch.js.map +1 -0
  83. package/dist/components/TyTab.d.ts +13 -0
  84. package/dist/components/TyTab.d.ts.map +1 -0
  85. package/dist/components/TyTab.js +32 -0
  86. package/dist/components/TyTab.js.map +1 -0
  87. package/dist/components/TyTabs.d.ts +23 -0
  88. package/dist/components/TyTabs.d.ts.map +1 -0
  89. package/dist/components/TyTabs.js +48 -0
  90. package/dist/components/TyTabs.js.map +1 -0
  91. package/dist/components/TyTag.d.ts +22 -0
  92. package/dist/components/TyTag.d.ts.map +1 -0
  93. package/dist/components/TyTag.js +45 -0
  94. package/dist/components/TyTag.js.map +1 -0
  95. package/dist/components/TyTextarea.d.ts +37 -0
  96. package/dist/components/TyTextarea.d.ts.map +1 -0
  97. package/dist/components/TyTextarea.js +113 -0
  98. package/dist/components/TyTextarea.js.map +1 -0
  99. package/dist/components/TyTooltip.d.ts +17 -0
  100. package/dist/components/TyTooltip.d.ts.map +1 -0
  101. package/dist/components/TyTooltip.js +40 -0
  102. package/dist/components/TyTooltip.js.map +1 -0
  103. package/dist/components/TyWizard.d.ts +26 -0
  104. package/dist/components/TyWizard.d.ts.map +1 -0
  105. package/dist/components/TyWizard.js +50 -0
  106. package/dist/components/TyWizard.js.map +1 -0
  107. package/dist/components/index.d.ts +105 -0
  108. package/dist/components/index.d.ts.map +1 -0
  109. package/dist/components/index.js +112 -0
  110. package/dist/components/index.js.map +1 -0
  111. package/dist/utils/react-version.d.ts +2 -0
  112. package/dist/utils/react-version.d.ts.map +1 -0
  113. package/dist/utils/react-version.js +8 -0
  114. package/dist/utils/react-version.js.map +1 -0
  115. package/package.json +46 -0
  116. package/src/components/EventConventionTest.tsx +155 -0
  117. package/src/components/TyButton.tsx +145 -0
  118. package/src/components/TyCalendar.tsx +248 -0
  119. package/src/components/TyCalendarMonth.tsx +108 -0
  120. package/src/components/TyCalendarNavigation.tsx +91 -0
  121. package/src/components/TyCheckbox.tsx +152 -0
  122. package/src/components/TyCopy.tsx +78 -0
  123. package/src/components/TyDatePicker.tsx +220 -0
  124. package/src/components/TyDropdown.tsx +225 -0
  125. package/src/components/TyIcon.tsx +72 -0
  126. package/src/components/TyInput.tsx +232 -0
  127. package/src/components/TyModal.tsx +142 -0
  128. package/src/components/TyMultiselect.tsx +203 -0
  129. package/src/components/TyOption.tsx +42 -0
  130. package/src/components/TyPopup.tsx +111 -0
  131. package/src/components/TyRadio.tsx +70 -0
  132. package/src/components/TyRadioGroup.tsx +121 -0
  133. package/src/components/TyResizeObserver.tsx +54 -0
  134. package/src/components/TyScrollContainer.tsx +87 -0
  135. package/src/components/TyStep.tsx +71 -0
  136. package/src/components/TySwitch.tsx +122 -0
  137. package/src/components/TyTab.tsx +63 -0
  138. package/src/components/TyTabs.tsx +93 -0
  139. package/src/components/TyTag.tsx +79 -0
  140. package/src/components/TyTextarea.tsx +177 -0
  141. package/src/components/TyTooltip.tsx +83 -0
  142. package/src/components/TyWizard.tsx +99 -0
  143. package/src/components/index.ts +251 -0
  144. package/src/utils/react-version.ts +8 -0
@@ -0,0 +1,145 @@
1
+ import React, { useEffect, useRef, useCallback } from 'react';
2
+
3
+ type BuiltinFlavor = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
4
+ type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`;
5
+ type ButtonAppearance = 'solid' | 'outlined' | 'ghost';
6
+
7
+ export interface TyButtonCSSProperties extends React.CSSProperties {
8
+ '--ty-button-bg'?: string;
9
+ '--ty-button-bg-hover'?: string;
10
+ '--ty-button-color'?: string;
11
+ '--ty-button-border'?: string;
12
+ }
13
+
14
+ export interface TyButtonProps extends Omit<React.HTMLAttributes<HTMLElement>, 'style'> {
15
+ style?: TyButtonCSSProperties;
16
+ /**
17
+ * Semantic styling variant. Built-in flavors get themed styles; append `+`
18
+ * for a stronger shade or `-` for a softer one (e.g. `"primary+"`,
19
+ * `"danger-"`). Any other string is passed through as-is — theme it via
20
+ * `--ty-button-*` CSS variables.
21
+ */
22
+ flavor?: ShadedFlavor | (string & {});
23
+
24
+ /**
25
+ * Visual appearance:
26
+ * - `"solid"` (default) — saturated brand fill with paired text color
27
+ * - `"outlined"` — transparent background, text === border
28
+ * - `"ghost"` — text only with hover background
29
+ */
30
+ appearance?: ButtonAppearance;
31
+
32
+ /** Button size */
33
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
34
+
35
+ /** Button type for form submission */
36
+ type?: 'button' | 'submit' | 'reset';
37
+
38
+ /** Disable the button */
39
+ disabled?: boolean;
40
+
41
+ /** Pill-shaped button (rounded ends) */
42
+ pill?: boolean;
43
+
44
+ /** Action (icon-only square) */
45
+ action?: boolean;
46
+
47
+ /** Accessible label for screen readers */
48
+ label?: string;
49
+
50
+ /** Form field name for form submission */
51
+ name?: string;
52
+
53
+ /** Form field value for form submission */
54
+ value?: string;
55
+
56
+ /** Full-width button */
57
+ wide?: boolean;
58
+
59
+ /** Button content */
60
+ children?: React.ReactNode;
61
+ }
62
+
63
+ export const TyButton = React.forwardRef<HTMLElement, TyButtonProps>(
64
+ ({
65
+ children,
66
+ type,
67
+ appearance,
68
+ disabled,
69
+ pill,
70
+ action,
71
+ wide,
72
+ label,
73
+ name,
74
+ value,
75
+ onClick,
76
+ ...props
77
+ }, ref) => {
78
+ const elementRef = useRef<HTMLElement>(null);
79
+
80
+ // Imperatively attach the click listener so onClick reliably fires for the
81
+ // CustomEvent('click') that <ty-button> re-dispatches on its host (the
82
+ // inner <button> calls stopPropagation, so React's delegated onClick can
83
+ // miss it). Also handles type=submit by dispatching a synthetic submit.
84
+ useEffect(() => {
85
+ const element = elementRef.current;
86
+ if (!element) return;
87
+
88
+ const handler = (event: Event) => {
89
+ if (type === 'submit') {
90
+ const form = element.closest('form');
91
+ if (form) {
92
+ event.preventDefault();
93
+ event.stopPropagation();
94
+ form.dispatchEvent(new Event('submit', {
95
+ bubbles: true,
96
+ cancelable: true,
97
+ }));
98
+ }
99
+ }
100
+ if (onClick) {
101
+ onClick(event as unknown as React.MouseEvent<HTMLElement>);
102
+ }
103
+ };
104
+
105
+ element.addEventListener('click', handler);
106
+ return () => {
107
+ element.removeEventListener('click', handler);
108
+ };
109
+ }, [type, onClick]);
110
+
111
+ useEffect(() => {
112
+ if (ref && elementRef.current) {
113
+ if (typeof ref === 'function') {
114
+ ref(elementRef.current);
115
+ } else {
116
+ ref.current = elementRef.current;
117
+ }
118
+ }
119
+ }, [ref]);
120
+
121
+ const webComponentProps: Record<string, any> = {
122
+ ...props,
123
+ ref: elementRef,
124
+ };
125
+
126
+ if (disabled) webComponentProps.disabled = '';
127
+ if (pill) webComponentProps.pill = '';
128
+ if (action) webComponentProps.action = '';
129
+ if (wide) webComponentProps.wide = '';
130
+
131
+ if (appearance) webComponentProps.appearance = appearance;
132
+ if (type) webComponentProps.type = type;
133
+ if (label) webComponentProps.label = label;
134
+ if (name) webComponentProps.name = name;
135
+ if (value) webComponentProps.value = value;
136
+
137
+ return React.createElement(
138
+ 'ty-button',
139
+ webComponentProps,
140
+ children
141
+ );
142
+ }
143
+ );
144
+
145
+ TyButton.displayName = 'TyButton';
@@ -0,0 +1,248 @@
1
+ import React, { useEffect, useRef, useCallback } from 'react';
2
+ import { needsPropertyBridge } from '../utils/react-version';
3
+
4
+ // Type definitions for Ty Calendar component
5
+ export interface TyCalendarChangeEventDetail {
6
+ /** Selected month (1-12) */
7
+ month: number;
8
+ /** Selected year (4-digit) */
9
+ year: number;
10
+ /** Selected day (1-31) */
11
+ day: number;
12
+ /** Action that triggered the change: "select" */
13
+ action: 'select';
14
+ /** Source of the change: "day-click" */
15
+ source: 'day-click';
16
+ /** Complete day context from the calendar month */
17
+ dayContext: any;
18
+ }
19
+
20
+ export interface TyCalendarNavigateEventDetail {
21
+ /** Navigation target month (1-12) */
22
+ month: number;
23
+ /** Navigation target year (4-digit) */
24
+ year: number;
25
+ /** Action that triggered the navigation: "navigate" */
26
+ action: 'navigate';
27
+ /** Source of the change: "navigation" */
28
+ source: 'navigation';
29
+ }
30
+
31
+ export interface TyCalendarProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
32
+ /** Selected year (4-digit) */
33
+ year?: number | string;
34
+
35
+ /** Selected month (1-12) */
36
+ month?: number | string;
37
+
38
+ /** Selected day (1-31) */
39
+ day?: number | string;
40
+
41
+ /** Show navigation controls */
42
+ showNavigation?: boolean;
43
+
44
+ /** Stateless mode - no internal state management */
45
+ stateless?: boolean;
46
+
47
+ /** Calendar size */
48
+ size?: 'sm' | 'md' | 'lg';
49
+
50
+ /** Locale for date formatting */
51
+ locale?: string;
52
+
53
+ /** Calendar width */
54
+ width?: string | number;
55
+
56
+ /** Minimum calendar width */
57
+ minWidth?: string | number;
58
+
59
+ /** Maximum calendar width */
60
+ maxWidth?: string | number;
61
+
62
+ /** Form field name for form submission */
63
+ name?: string;
64
+
65
+ /** Form value (ISO date string) */
66
+ value?: string;
67
+
68
+ /** Function to render custom day content */
69
+ dayContentFn?: (dayContext: any) => HTMLElement | string;
70
+
71
+ /** Function to determine day CSS classes */
72
+ dayClassesFn?: (dayContext: any) => string[];
73
+
74
+ /** Custom CSS injection for render functions */
75
+ customCSS?: string;
76
+
77
+ /** Callback when a date is selected */
78
+ onChange?: (event: CustomEvent<TyCalendarChangeEventDetail>) => void;
79
+
80
+ /** Callback when navigation changes month/year */
81
+ onNavigate?: (event: CustomEvent<TyCalendarNavigateEventDetail>) => void;
82
+ }
83
+
84
+ // React wrapper for ty-calendar web component
85
+ export const TyCalendar = React.forwardRef<HTMLElement, TyCalendarProps>(
86
+ ({
87
+ year,
88
+ month,
89
+ day,
90
+ showNavigation,
91
+ stateless,
92
+ size,
93
+ locale,
94
+ width,
95
+ minWidth,
96
+ maxWidth,
97
+ name,
98
+ value,
99
+ dayContentFn,
100
+ dayClassesFn,
101
+ customCSS,
102
+ onChange,
103
+ onNavigate,
104
+ ...props
105
+ }, ref) => {
106
+ const elementRef = useRef<HTMLElement>(null);
107
+
108
+ // Handle ref forwarding
109
+ useEffect(() => {
110
+ if (ref && elementRef.current) {
111
+ if (typeof ref === 'function') {
112
+ ref(elementRef.current);
113
+ } else {
114
+ ref.current = elementRef.current;
115
+ }
116
+ }
117
+ }, [ref]);
118
+
119
+ // Handle change events (date selection)
120
+ const handleChange = useCallback((event: Event) => {
121
+ const customEvent = event as CustomEvent<TyCalendarChangeEventDetail>;
122
+ if (onChange) {
123
+ onChange(customEvent);
124
+ }
125
+ }, [onChange]);
126
+
127
+ // Handle navigate events (month/year navigation)
128
+ const handleNavigate = useCallback((event: Event) => {
129
+ const customEvent = event as CustomEvent<TyCalendarNavigateEventDetail>;
130
+ if (onNavigate) {
131
+ onNavigate(customEvent);
132
+ }
133
+ }, [onNavigate]);
134
+
135
+ // Set up event listeners
136
+ useEffect(() => {
137
+ const element = elementRef.current;
138
+ if (!element) return;
139
+
140
+ const listeners: Array<[string, EventListener]> = [];
141
+
142
+ if (onChange) {
143
+ element.addEventListener('change', handleChange);
144
+ listeners.push(['change', handleChange]);
145
+ }
146
+
147
+ if (onNavigate) {
148
+ element.addEventListener('navigate', handleNavigate);
149
+ listeners.push(['navigate', handleNavigate]);
150
+ }
151
+
152
+ return () => {
153
+ listeners.forEach(([eventName, handler]) => {
154
+ element.removeEventListener(eventName, handler);
155
+ });
156
+ };
157
+ }, [handleChange, handleNavigate, onChange, onNavigate]);
158
+
159
+ // Set function/object properties directly on the element. Required on
160
+ // React 18, which can't bridge non-string props onto custom elements.
161
+ // React 19+ handles function/object prop-to-property bridging natively.
162
+ useEffect(() => {
163
+ if (!needsPropertyBridge) return;
164
+ const element = elementRef.current;
165
+ if (!element) return;
166
+
167
+ // Day content function property (preferred over attribute)
168
+ if (dayContentFn) {
169
+ (element as any).dayContentFn = dayContentFn;
170
+ } else {
171
+ (element as any).dayContentFn = null;
172
+ }
173
+
174
+ // Day classes function property (preferred over attribute)
175
+ if (dayClassesFn) {
176
+ (element as any).dayClassesFn = dayClassesFn;
177
+ } else {
178
+ (element as any).dayClassesFn = null;
179
+ }
180
+
181
+ // Custom CSS property
182
+ if (customCSS) {
183
+ (element as any).customCSS = customCSS;
184
+ } else {
185
+ (element as any).customCSS = null;
186
+ }
187
+ }, [dayContentFn, dayClassesFn, customCSS]);
188
+
189
+ // Convert React props to web component attributes
190
+ const webComponentProps: Record<string, any> = {
191
+ ...props,
192
+ ref: elementRef,
193
+ };
194
+
195
+ // Add optional attributes only if they have values
196
+ if (year !== undefined) {
197
+ webComponentProps.year = year.toString();
198
+ }
199
+
200
+ if (month !== undefined) {
201
+ webComponentProps.month = month.toString();
202
+ }
203
+
204
+ if (day !== undefined) {
205
+ webComponentProps.day = day.toString();
206
+ }
207
+
208
+ if (showNavigation) {
209
+ webComponentProps['show-navigation'] = ''; // Boolean attributes as empty string
210
+ }
211
+
212
+ if (stateless) {
213
+ webComponentProps.stateless = ''; // Boolean attributes as empty string
214
+ }
215
+
216
+ if (size) {
217
+ webComponentProps.size = size;
218
+ }
219
+
220
+ if (locale) {
221
+ webComponentProps.locale = locale;
222
+ }
223
+
224
+ if (width !== undefined) {
225
+ webComponentProps.width = width.toString();
226
+ }
227
+
228
+ if (minWidth !== undefined) {
229
+ webComponentProps['min-width'] = minWidth.toString();
230
+ }
231
+
232
+ if (maxWidth !== undefined) {
233
+ webComponentProps['max-width'] = maxWidth.toString();
234
+ }
235
+
236
+ if (name) {
237
+ webComponentProps.name = name;
238
+ }
239
+
240
+ if (value) {
241
+ webComponentProps.value = value;
242
+ }
243
+
244
+ return React.createElement('ty-calendar', webComponentProps);
245
+ }
246
+ );
247
+
248
+ TyCalendar.displayName = 'TyCalendar';
@@ -0,0 +1,108 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ // Type definitions for Ty Calendar Month component
4
+ export interface TyCalendarMonthProps extends React.HTMLAttributes<HTMLElement> {
5
+ /** Display year */
6
+ displayYear?: number;
7
+
8
+ /** Display month (1-12) */
9
+ displayMonth?: number;
10
+
11
+ /** Locale for date formatting */
12
+ locale?: string;
13
+
14
+ /** Calendar size */
15
+ size?: 'sm' | 'md' | 'lg';
16
+
17
+ /** Width of calendar */
18
+ width?: string;
19
+
20
+ /** Minimum width */
21
+ minWidth?: string;
22
+
23
+ /** Maximum width */
24
+ maxWidth?: string;
25
+
26
+ /** Day click event handler */
27
+ onDayClick?: (event: CustomEvent<DayClickDetail>) => void;
28
+ }
29
+
30
+ export interface DayClickDetail {
31
+ dayContext: any; // DayContext type from calendar-utils
32
+ value: number;
33
+ year: number;
34
+ month: number;
35
+ day: number;
36
+ isHoliday?: boolean;
37
+ isToday?: boolean;
38
+ isWeekend: boolean;
39
+ isOtherMonth: boolean;
40
+ }
41
+
42
+ // React wrapper for ty-calendar-month web component
43
+ export const TyCalendarMonth = React.forwardRef<HTMLElement, TyCalendarMonthProps>(
44
+ ({
45
+ displayYear,
46
+ displayMonth,
47
+ locale,
48
+ size,
49
+ width,
50
+ minWidth,
51
+ maxWidth,
52
+ onDayClick,
53
+ ...props
54
+ }, ref) => {
55
+ const elementRef = useRef<HTMLElement>(null);
56
+
57
+ // Handle day click events
58
+ useEffect(() => {
59
+ const element = elementRef.current;
60
+ if (!element) return;
61
+
62
+ const handleDayClick = (event: Event) => {
63
+ if (onDayClick) {
64
+ onDayClick(event as CustomEvent<DayClickDetail>);
65
+ }
66
+ };
67
+
68
+ element.addEventListener('day-click', handleDayClick);
69
+
70
+ return () => {
71
+ element.removeEventListener('day-click', handleDayClick);
72
+ };
73
+ }, [onDayClick]);
74
+
75
+ // Combine refs if needed
76
+ useEffect(() => {
77
+ if (ref && elementRef.current) {
78
+ if (typeof ref === 'function') {
79
+ ref(elementRef.current);
80
+ } else {
81
+ ref.current = elementRef.current;
82
+ }
83
+ }
84
+ }, [ref]);
85
+
86
+ // Convert React props to web component attributes
87
+ const webComponentProps: Record<string, any> = {
88
+ ...props,
89
+ ref: elementRef,
90
+ };
91
+
92
+ // Add attributes
93
+ if (displayYear !== undefined) webComponentProps['display-year'] = displayYear;
94
+ if (displayMonth !== undefined) webComponentProps['display-month'] = displayMonth;
95
+ if (locale) webComponentProps.locale = locale;
96
+ if (size) webComponentProps.size = size;
97
+ if (width) webComponentProps.width = width;
98
+ if (minWidth) webComponentProps['min-width'] = minWidth;
99
+ if (maxWidth) webComponentProps['max-width'] = maxWidth;
100
+
101
+ return React.createElement(
102
+ 'ty-calendar-month',
103
+ webComponentProps
104
+ );
105
+ }
106
+ );
107
+
108
+ TyCalendarMonth.displayName = 'TyCalendarMonth';
@@ -0,0 +1,91 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+
3
+ // Type definitions for Ty Calendar Navigation component
4
+ export interface TyCalendarNavigationProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
5
+ /** Display month (1-12) */
6
+ displayMonth?: number;
7
+
8
+ /** Display year */
9
+ displayYear?: number;
10
+
11
+ /** Locale for month name formatting */
12
+ locale?: string;
13
+
14
+ /** Navigation size */
15
+ size?: 'sm' | 'md' | 'lg';
16
+
17
+ /** Width of navigation */
18
+ width?: string;
19
+
20
+ /** Navigation change event handler */
21
+ onChange?: (event: CustomEvent<NavigationChangeDetail>) => void;
22
+ }
23
+
24
+ export interface NavigationChangeDetail {
25
+ month: number; // 1-12
26
+ year: number; // e.g., 2025
27
+ }
28
+
29
+ // React wrapper for ty-calendar-navigation web component
30
+ export const TyCalendarNavigation = React.forwardRef<HTMLElement, TyCalendarNavigationProps>(
31
+ ({
32
+ displayMonth,
33
+ displayYear,
34
+ locale,
35
+ size,
36
+ width,
37
+ onChange,
38
+ ...props
39
+ }, ref) => {
40
+ const elementRef = useRef<HTMLElement>(null);
41
+
42
+ // Handle change events
43
+ useEffect(() => {
44
+ const element = elementRef.current;
45
+ if (!element) return;
46
+
47
+ const handleChange = (event: Event) => {
48
+ if (onChange) {
49
+ onChange(event as CustomEvent<NavigationChangeDetail>);
50
+ }
51
+ };
52
+
53
+ element.addEventListener('change', handleChange);
54
+
55
+ return () => {
56
+ element.removeEventListener('change', handleChange);
57
+ };
58
+ }, [onChange]);
59
+
60
+ // Combine refs if needed
61
+ useEffect(() => {
62
+ if (ref && elementRef.current) {
63
+ if (typeof ref === 'function') {
64
+ ref(elementRef.current);
65
+ } else {
66
+ ref.current = elementRef.current;
67
+ }
68
+ }
69
+ }, [ref]);
70
+
71
+ // Convert React props to web component attributes
72
+ const webComponentProps: Record<string, any> = {
73
+ ...props,
74
+ ref: elementRef,
75
+ };
76
+
77
+ // Add attributes
78
+ if (displayMonth !== undefined) webComponentProps['display-month'] = displayMonth;
79
+ if (displayYear !== undefined) webComponentProps['display-year'] = displayYear;
80
+ if (locale) webComponentProps.locale = locale;
81
+ if (size) webComponentProps.size = size;
82
+ if (width) webComponentProps.width = width;
83
+
84
+ return React.createElement(
85
+ 'ty-calendar-navigation',
86
+ webComponentProps
87
+ );
88
+ }
89
+ );
90
+
91
+ TyCalendarNavigation.displayName = 'TyCalendarNavigation';