tyrell-react 1.0.0-RC11 → 1.0.0-RC13

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 (142) hide show
  1. package/README.md +1 -0
  2. package/dist/components/TyButton.d.ts.map +1 -1
  3. package/dist/components/TyButton.js +2 -1
  4. package/dist/components/TyButton.js.map +1 -1
  5. package/dist/components/TyCalendar.d.ts +11 -0
  6. package/dist/components/TyCalendar.d.ts.map +1 -1
  7. package/dist/components/TyCalendar.js +21 -3
  8. package/dist/components/TyCalendar.js.map +1 -1
  9. package/dist/components/TyCalendarMonth.d.ts +9 -0
  10. package/dist/components/TyCalendarMonth.d.ts.map +1 -1
  11. package/dist/components/TyCalendarMonth.js +9 -2
  12. package/dist/components/TyCalendarMonth.js.map +1 -1
  13. package/dist/components/TyCalendarNavigation.d.ts +4 -0
  14. package/dist/components/TyCalendarNavigation.d.ts.map +1 -1
  15. package/dist/components/TyCalendarNavigation.js +7 -2
  16. package/dist/components/TyCalendarNavigation.js.map +1 -1
  17. package/dist/components/TyCheckbox.d.ts +6 -1
  18. package/dist/components/TyCheckbox.d.ts.map +1 -1
  19. package/dist/components/TyCheckbox.js +6 -2
  20. package/dist/components/TyCheckbox.js.map +1 -1
  21. package/dist/components/TyCopy.d.ts +8 -1
  22. package/dist/components/TyCopy.d.ts.map +1 -1
  23. package/dist/components/TyCopy.js +21 -2
  24. package/dist/components/TyCopy.js.map +1 -1
  25. package/dist/components/TyDatePicker.d.ts +15 -4
  26. package/dist/components/TyDatePicker.d.ts.map +1 -1
  27. package/dist/components/TyDatePicker.js +9 -2
  28. package/dist/components/TyDatePicker.js.map +1 -1
  29. package/dist/components/TyFileUpload.d.ts.map +1 -1
  30. package/dist/components/TyFileUpload.js +2 -1
  31. package/dist/components/TyFileUpload.js.map +1 -1
  32. package/dist/components/TyInput.d.ts +6 -2
  33. package/dist/components/TyInput.d.ts.map +1 -1
  34. package/dist/components/TyInput.js +2 -1
  35. package/dist/components/TyInput.js.map +1 -1
  36. package/dist/components/TyModal.d.ts +5 -0
  37. package/dist/components/TyModal.d.ts.map +1 -1
  38. package/dist/components/TyModal.js +5 -2
  39. package/dist/components/TyModal.js.map +1 -1
  40. package/dist/components/TyOption.d.ts +8 -0
  41. package/dist/components/TyOption.d.ts.map +1 -1
  42. package/dist/components/TyOption.js +5 -2
  43. package/dist/components/TyOption.js.map +1 -1
  44. package/dist/components/TyPopup.d.ts.map +1 -1
  45. package/dist/components/TyPopup.js +2 -1
  46. package/dist/components/TyPopup.js.map +1 -1
  47. package/dist/components/TyRadio.d.ts +4 -1
  48. package/dist/components/TyRadio.d.ts.map +1 -1
  49. package/dist/components/TyRadio.js +2 -1
  50. package/dist/components/TyRadio.js.map +1 -1
  51. package/dist/components/TyRadioGroup.d.ts +4 -1
  52. package/dist/components/TyRadioGroup.d.ts.map +1 -1
  53. package/dist/components/TyRadioGroup.js +2 -1
  54. package/dist/components/TyRadioGroup.js.map +1 -1
  55. package/dist/components/TyResizeObserver.d.ts.map +1 -1
  56. package/dist/components/TyResizeObserver.js +2 -1
  57. package/dist/components/TyResizeObserver.js.map +1 -1
  58. package/dist/components/TyScrollContainer.d.ts +26 -1
  59. package/dist/components/TyScrollContainer.d.ts.map +1 -1
  60. package/dist/components/TyScrollContainer.js +43 -25
  61. package/dist/components/TyScrollContainer.js.map +1 -1
  62. package/dist/components/TySelect.d.ts +114 -0
  63. package/dist/components/TySelect.d.ts.map +1 -0
  64. package/dist/components/TySelect.js +159 -0
  65. package/dist/components/TySelect.js.map +1 -0
  66. package/dist/components/TyStep.d.ts.map +1 -1
  67. package/dist/components/TyStep.js +2 -1
  68. package/dist/components/TyStep.js.map +1 -1
  69. package/dist/components/TySwitch.d.ts +4 -1
  70. package/dist/components/TySwitch.d.ts.map +1 -1
  71. package/dist/components/TySwitch.js +2 -1
  72. package/dist/components/TySwitch.js.map +1 -1
  73. package/dist/components/TyTab.d.ts.map +1 -1
  74. package/dist/components/TyTab.js +2 -1
  75. package/dist/components/TyTab.js.map +1 -1
  76. package/dist/components/TyTabs.d.ts.map +1 -1
  77. package/dist/components/TyTabs.js +2 -1
  78. package/dist/components/TyTabs.js.map +1 -1
  79. package/dist/components/TyTag.d.ts +4 -1
  80. package/dist/components/TyTag.d.ts.map +1 -1
  81. package/dist/components/TyTag.js +2 -1
  82. package/dist/components/TyTag.js.map +1 -1
  83. package/dist/components/TyTextarea.d.ts.map +1 -1
  84. package/dist/components/TyTextarea.js +2 -1
  85. package/dist/components/TyTextarea.js.map +1 -1
  86. package/dist/components/TyTooltip.d.ts +4 -1
  87. package/dist/components/TyTooltip.d.ts.map +1 -1
  88. package/dist/components/TyTooltip.js +2 -1
  89. package/dist/components/TyTooltip.js.map +1 -1
  90. package/dist/components/TyWizard.d.ts.map +1 -1
  91. package/dist/components/TyWizard.js +2 -1
  92. package/dist/components/TyWizard.js.map +1 -1
  93. package/dist/components/index.d.ts +6 -8
  94. package/dist/components/index.d.ts.map +1 -1
  95. package/dist/components/index.js +5 -4
  96. package/dist/components/index.js.map +1 -1
  97. package/dist/utils/host-props.d.ts +11 -0
  98. package/dist/utils/host-props.d.ts.map +1 -0
  99. package/dist/utils/host-props.js +16 -0
  100. package/dist/utils/host-props.js.map +1 -0
  101. package/dist/version.d.ts +1 -1
  102. package/dist/version.js +1 -1
  103. package/package.json +1 -1
  104. package/src/components/TyButton.tsx +2 -1
  105. package/src/components/TyCalendar.tsx +44 -3
  106. package/src/components/TyCalendarMonth.tsx +22 -3
  107. package/src/components/TyCalendarNavigation.tsx +13 -2
  108. package/src/components/TyCheckbox.tsx +14 -4
  109. package/src/components/TyCopy.tsx +31 -3
  110. package/src/components/TyDatePicker.tsx +31 -6
  111. package/src/components/TyFileUpload.tsx +2 -1
  112. package/src/components/TyInput.tsx +8 -3
  113. package/src/components/TyModal.tsx +10 -1
  114. package/src/components/TyOption.tsx +16 -2
  115. package/src/components/TyPopup.tsx +2 -1
  116. package/src/components/TyRadio.tsx +6 -2
  117. package/src/components/TyRadioGroup.tsx +6 -2
  118. package/src/components/TyResizeObserver.tsx +2 -1
  119. package/src/components/TyScrollContainer.tsx +76 -25
  120. package/src/components/TySelect.tsx +305 -0
  121. package/src/components/TyStep.tsx +2 -1
  122. package/src/components/TySwitch.tsx +6 -2
  123. package/src/components/TyTab.tsx +2 -1
  124. package/src/components/TyTabs.tsx +2 -1
  125. package/src/components/TyTag.tsx +6 -2
  126. package/src/components/TyTextarea.tsx +2 -1
  127. package/src/components/TyTooltip.tsx +6 -2
  128. package/src/components/TyWizard.tsx +2 -1
  129. package/src/components/index.ts +8 -13
  130. package/src/utils/host-props.ts +14 -0
  131. package/src/version.ts +1 -1
  132. package/dist/components/TyDropdown.d.ts +0 -62
  133. package/dist/components/TyDropdown.d.ts.map +0 -1
  134. package/dist/components/TyDropdown.js +0 -124
  135. package/dist/components/TyDropdown.js.map +0 -1
  136. package/dist/components/TyMultiselect.d.ts +0 -57
  137. package/dist/components/TyMultiselect.d.ts.map +0 -1
  138. package/dist/components/TyMultiselect.js +0 -111
  139. package/dist/components/TyMultiselect.js.map +0 -1
  140. package/src/components/EventConventionTest.tsx +0 -155
  141. package/src/components/TyDropdown.tsx +0 -240
  142. package/src/components/TyMultiselect.tsx +0 -208
@@ -1,6 +1,10 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { useBooleanProperty } from '../utils/use-boolean-prop';
3
4
 
5
+ type BuiltinFlavor = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
6
+ type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`;
7
+
4
8
  // Type definitions for Ty Copy component
5
9
  export interface TyCopyProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
6
10
  /** Text to copy */
@@ -13,7 +17,7 @@ export interface TyCopyProps extends Omit<React.HTMLAttributes<HTMLElement>, 'on
13
17
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
14
18
 
15
19
  /** Semantic styling variant */
16
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
20
+ flavor?: ShadedFlavor | (string & {});
17
21
 
18
22
  /** Display format */
19
23
  format?: 'text' | 'code';
@@ -26,6 +30,12 @@ export interface TyCopyProps extends Omit<React.HTMLAttributes<HTMLElement>, 'on
26
30
 
27
31
  /** Required field */
28
32
  required?: boolean;
33
+
34
+ /** Fired after the value is copied to the clipboard */
35
+ onCopySuccess?: (event: CustomEvent) => void;
36
+
37
+ /** Fired when copying to the clipboard fails */
38
+ onCopyError?: (event: CustomEvent) => void;
29
39
  }
30
40
 
31
41
  // React wrapper for ty-copy web component
@@ -39,7 +49,9 @@ export const TyCopy = React.forwardRef<HTMLElement, TyCopyProps>(
39
49
  multiline,
40
50
  disabled,
41
51
  required,
42
- ...props
52
+ onCopySuccess,
53
+ onCopyError,
54
+ ...props
43
55
  }, ref) => {
44
56
  const elementRef = useRef<HTMLElement>(null);
45
57
 
@@ -54,13 +66,29 @@ export const TyCopy = React.forwardRef<HTMLElement, TyCopyProps>(
54
66
  }
55
67
  }, [ref]);
56
68
 
69
+ // Custom events → React callbacks
70
+ useEffect(() => {
71
+ const el = elementRef.current;
72
+ if (!el) return;
73
+ const bound: Array<[string, EventListener]> = [];
74
+ if (onCopySuccess) {
75
+ const h = (e: Event) => onCopySuccess(e as CustomEvent);
76
+ el.addEventListener('copy-success', h); bound.push(['copy-success', h]);
77
+ }
78
+ if (onCopyError) {
79
+ const h = (e: Event) => onCopyError(e as CustomEvent);
80
+ el.addEventListener('copy-error', h); bound.push(['copy-error', h]);
81
+ }
82
+ return () => bound.forEach(([n, h]) => el.removeEventListener(n, h));
83
+ }, [onCopySuccess, onCopyError]);
84
+
57
85
  const isMultiline = useBooleanProperty(elementRef, 'multiline', multiline);
58
86
  const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
59
87
  const isRequired = useBooleanProperty(elementRef, 'required', required);
60
88
 
61
89
  // Convert React props to web component attributes
62
90
  const webComponentProps: Record<string, any> = {
63
- ...props,
91
+ ...hostProps(props),
64
92
  ref: elementRef,
65
93
  };
66
94
 
@@ -1,7 +1,11 @@
1
1
  import React, { useEffect, useRef, useCallback } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { needsPropertyBridge } from '../utils/react-version';
3
4
  import { useBooleanProperty } from '../utils/use-boolean-prop';
4
5
 
6
+ type BuiltinFlavor = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
7
+ type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`;
8
+
5
9
  // Type definitions for Ty DatePicker component
6
10
  export interface TyDatePickerEventDetail {
7
11
  /** The selected date value (ISO string or formatted string based on format) */
@@ -18,11 +22,11 @@ export interface TyDatePickerProps extends Omit<React.HTMLAttributes<HTMLElement
18
22
  /** The selected date value (ISO string or formatted string) */
19
23
  value?: string;
20
24
 
21
- /** Input size: "sm" | "md" | "lg" */
25
+ /** Field size fields come in exactly three; legacy xs/xl map to sm/lg */
22
26
  size?: 'sm' | 'md' | 'lg';
23
27
 
24
- /** Visual flavor: "default" | "success" | "danger" | "warning" */
25
- flavor?: 'default' | 'success' | 'danger' | 'warning';
28
+ /** Visual flavor built-in semantic, +/- shade, or a custom flavor backed by --ty-*-X tokens. Also tints the popup calendar's selected/today day. */
29
+ flavor?: 'default' | ShadedFlavor | (string & {});
26
30
 
27
31
  /** Label text displayed above the input */
28
32
  label?: string;
@@ -50,6 +54,12 @@ export interface TyDatePickerProps extends Omit<React.HTMLAttributes<HTMLElement
50
54
 
51
55
  /** Whether to include time selection */
52
56
  withTime?: boolean;
57
+
58
+ /** Earliest selectable date (ISO "YYYY-MM-DD") */
59
+ min?: string;
60
+
61
+ /** Latest selectable date (ISO "YYYY-MM-DD") */
62
+ max?: string;
53
63
 
54
64
  /** Callback when the date value changes */
55
65
  onChange?: (event: CustomEvent<TyDatePickerEventDetail>) => void;
@@ -61,8 +71,13 @@ export interface TyDatePickerProps extends Omit<React.HTMLAttributes<HTMLElement
61
71
  onClose?: (event: CustomEvent<{}>) => void;
62
72
  }
63
73
 
74
+ /** The underlying custom element, typed with its imperative clear() method. */
75
+ export interface TyDatePickerElement extends HTMLElement {
76
+ clear(): void;
77
+ }
78
+
64
79
  // React wrapper for ty-date-picker web component
65
- export const TyDatePicker = React.forwardRef<HTMLElement, TyDatePickerProps>(
80
+ export const TyDatePicker = React.forwardRef<TyDatePickerElement, TyDatePickerProps>(
66
81
  ({
67
82
  value,
68
83
  size,
@@ -76,12 +91,14 @@ export const TyDatePicker = React.forwardRef<HTMLElement, TyDatePickerProps>(
76
91
  format,
77
92
  locale,
78
93
  withTime,
94
+ min,
95
+ max,
79
96
  onChange,
80
97
  onOpen,
81
98
  onClose,
82
99
  ...props
83
100
  }, ref) => {
84
- const elementRef = useRef<HTMLElement>(null);
101
+ const elementRef = useRef<TyDatePickerElement>(null);
85
102
 
86
103
  // Handle ref forwarding
87
104
  useEffect(() => {
@@ -159,7 +176,7 @@ export const TyDatePicker = React.forwardRef<HTMLElement, TyDatePickerProps>(
159
176
 
160
177
  // Convert React props to web component attributes
161
178
  const webComponentProps: Record<string, any> = {
162
- ...props,
179
+ ...hostProps(props),
163
180
  ref: elementRef,
164
181
  };
165
182
 
@@ -208,6 +225,14 @@ export const TyDatePicker = React.forwardRef<HTMLElement, TyDatePickerProps>(
208
225
  webComponentProps['with-time'] = ''; // Convert camelCase to kebab-case
209
226
  }
210
227
 
228
+ if (min) {
229
+ webComponentProps.min = min;
230
+ }
231
+
232
+ if (max) {
233
+ webComponentProps.max = max;
234
+ }
235
+
211
236
  return React.createElement('ty-date-picker', webComponentProps);
212
237
  }
213
238
  );
@@ -1,4 +1,5 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { useBooleanProperty } from '../utils/use-boolean-prop';
3
4
 
4
5
  // Type definitions for Ty File Upload component
@@ -87,7 +88,7 @@ export const TyFileUpload = React.forwardRef<HTMLElement, TyFileUploadProps>(
87
88
  const isRequired = useBooleanProperty(elementRef, 'required', required);
88
89
 
89
90
  const webComponentProps: Record<string, any> = {
90
- ...props,
91
+ ...hostProps(props),
91
92
  ref: elementRef,
92
93
  };
93
94
 
@@ -1,7 +1,11 @@
1
1
  import React, { useEffect, useRef, useCallback } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { needsPropertyBridge } from '../utils/react-version';
3
4
  import { useBooleanProperty } from '../utils/use-boolean-prop';
4
5
 
6
+ type BuiltinFlavor = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
7
+ type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`;
8
+
5
9
  // Event detail structure for ty-input events
6
10
  export interface TyInputEventDetail {
7
11
  value: any; // shadow value (processed/parsed)
@@ -31,10 +35,11 @@ export interface TyInputProps extends Omit<React.HTMLAttributes<HTMLElement>, 'o
31
35
  | 'currency' | 'percent' | 'compact';
32
36
 
33
37
  /** Semantic styling variant */
34
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
38
+ flavor?: ShadedFlavor | (string & {});
35
39
 
36
40
  /** Input size */
37
- size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
41
+ /** Field size fields come in exactly three; legacy xs/xl map to sm/lg */
42
+ size?: 'sm' | 'md' | 'lg';
38
43
 
39
44
  /** Input value */
40
45
  value?: string;
@@ -214,7 +219,7 @@ export const TyInput = React.forwardRef<HTMLElement, TyInputProps>(
214
219
 
215
220
  // Convert React props to web component attributes
216
221
  const webComponentProps: Record<string, any> = {
217
- ...props,
222
+ ...hostProps(props),
218
223
  ref: elementRef,
219
224
  };
220
225
 
@@ -1,4 +1,5 @@
1
1
  import React, { useEffect, useRef, useImperativeHandle } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { useBooleanProperty, coerceBool } from '../utils/use-boolean-prop';
3
4
  import { needsPropertyBridge } from '../utils/react-version';
4
5
 
@@ -32,6 +33,12 @@ export interface TyModalProps extends React.HTMLAttributes<HTMLElement> {
32
33
  /** Allow closing modal with Escape key (default: true) */
33
34
  closeOnEscape?: boolean;
34
35
 
36
+ /**
37
+ * Accessible name — sets aria-label on the internal <dialog>. The dialog
38
+ * gets role="dialog" for free, but nothing names it unless you set this.
39
+ */
40
+ label?: string;
41
+
35
42
  /** React event handlers */
36
43
  onOpen?: (event: CustomEvent<TyModalEventDetail>) => void;
37
44
  onClose?: (event: CustomEvent<TyModalEventDetail>) => void;
@@ -64,6 +71,7 @@ export const TyModal = React.forwardRef<TyModalRef, TyModalProps>(
64
71
  backdrop,
65
72
  closeOnOutsideClick,
66
73
  closeOnEscape,
74
+ label,
67
75
  onOpen,
68
76
  onClose,
69
77
  onBeforeClose,
@@ -166,7 +174,7 @@ export const TyModal = React.forwardRef<TyModalRef, TyModalProps>(
166
174
 
167
175
  // Convert React props to web component attributes
168
176
  const webComponentProps: Record<string, any> = {
169
- ...props,
177
+ ...hostProps(props),
170
178
  ref: elementRef,
171
179
  };
172
180
 
@@ -183,6 +191,7 @@ export const TyModal = React.forwardRef<TyModalRef, TyModalProps>(
183
191
  if (closeOnEscape !== undefined && !coerceBool(closeOnEscape)) {
184
192
  webComponentProps['close-on-escape'] = 'false';
185
193
  }
194
+ if (label) webComponentProps.label = label;
186
195
 
187
196
  return React.createElement(
188
197
  'ty-modal',
@@ -1,9 +1,21 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { useBooleanProperty } from '../utils/use-boolean-prop';
3
4
 
4
5
  // Type definitions for Ty Option component
5
6
  export interface TyOptionProps extends React.HTMLAttributes<HTMLElement> {
6
7
  value?: string;
8
+
9
+ /**
10
+ * Clean display text (native <option label> semantics) — used by ty-select
11
+ * for field summaries and by ty-selected-options chips when the option's
12
+ * children are rich HTML. data-* attributes feed chip templates.
13
+ */
14
+ label?: string;
15
+
16
+ /** Semantic flavor — carried onto ty-selected-options chips */
17
+ flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral' | 'info';
18
+
7
19
  disabled?: boolean;
8
20
  selected?: boolean;
9
21
  hidden?: boolean;
@@ -12,7 +24,7 @@ export interface TyOptionProps extends React.HTMLAttributes<HTMLElement> {
12
24
 
13
25
  // React wrapper for ty-option web component
14
26
  export const TyOption = React.forwardRef<HTMLElement, TyOptionProps>(
15
- ({ children, disabled, selected, hidden, ...props }, ref) => {
27
+ ({ children, label, flavor, disabled, selected, hidden, ...props }, ref) => {
16
28
  const elementRef = useRef<HTMLElement>(null);
17
29
 
18
30
  // Handle ref forwarding
@@ -33,7 +45,9 @@ export const TyOption = React.forwardRef<HTMLElement, TyOptionProps>(
33
45
  return React.createElement(
34
46
  'ty-option',
35
47
  {
36
- ...props,
48
+ ...hostProps(props),
49
+ ...(label && { label }),
50
+ ...(flavor && { flavor }),
37
51
  ...(isDisabled && { disabled: "" }),
38
52
  ...(isSelected && { selected: "" }),
39
53
  ...(isHidden && { hidden: "" }),
@@ -1,4 +1,5 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { useBooleanProperty } from '../utils/use-boolean-prop';
3
4
 
4
5
  // Type definitions for Ty Popup component
@@ -86,7 +87,7 @@ export const TyPopup = React.forwardRef<TyPopupElement, TyPopupProps>(
86
87
 
87
88
  // Convert React props to web component attributes
88
89
  const webComponentProps: Record<string, any> = {
89
- ...props,
90
+ ...hostProps(props),
90
91
  ref: elementRef,
91
92
  };
92
93
 
@@ -1,6 +1,10 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { useBooleanProperty } from '../utils/use-boolean-prop';
3
4
 
5
+ type BuiltinFlavor = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
6
+ type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`;
7
+
4
8
  export interface TyRadioProps extends React.HTMLAttributes<HTMLElement> {
5
9
  /** Form field value (selected by parent ty-radio-group when matches its `value`) */
6
10
  value?: string;
@@ -18,7 +22,7 @@ export interface TyRadioProps extends React.HTMLAttributes<HTMLElement> {
18
22
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
19
23
 
20
24
  /** Semantic styling variant — typically inherited from the parent group */
21
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
25
+ flavor?: ShadedFlavor | (string & {});
22
26
 
23
27
  /** Label content (wrap in a `<label>` for click delegation, see ty-radio docs) */
24
28
  children?: React.ReactNode;
@@ -43,7 +47,7 @@ export const TyRadio = React.forwardRef<HTMLElement, TyRadioProps>(
43
47
  const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
44
48
 
45
49
  const webComponentProps: Record<string, any> = {
46
- ...props,
50
+ ...hostProps(props),
47
51
  ref: elementRef,
48
52
  };
49
53
 
@@ -1,6 +1,10 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { useBooleanProperty } from '../utils/use-boolean-prop';
3
4
 
5
+ type BuiltinFlavor = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
6
+ type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`;
7
+
4
8
  export interface TyRadioGroupProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'onInput'> {
5
9
  /** Currently selected value (matches one child `<TyRadio value="...">`) */
6
10
  value?: string;
@@ -27,7 +31,7 @@ export interface TyRadioGroupProps extends Omit<React.HTMLAttributes<HTMLElement
27
31
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
28
32
 
29
33
  /** Group flavor — propagates to all `<TyRadio>` children */
30
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
34
+ flavor?: ShadedFlavor | (string & {});
31
35
 
32
36
  /**
33
37
  * Fires when selection changes (React convention)
@@ -103,7 +107,7 @@ export const TyRadioGroup = React.forwardRef<HTMLElement, TyRadioGroupProps>(
103
107
  const isRequired = useBooleanProperty(elementRef, 'required', required);
104
108
 
105
109
  const webComponentProps: Record<string, any> = {
106
- ...props,
110
+ ...hostProps(props),
107
111
  ref: elementRef,
108
112
  };
109
113
 
@@ -1,4 +1,5 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
 
3
4
  // Type definitions for Ty ResizeObserver component
4
5
  export interface TyResizeObserverProps extends React.HTMLAttributes<HTMLElement> {
@@ -35,7 +36,7 @@ export const TyResizeObserver = React.forwardRef<HTMLElement, TyResizeObserverPr
35
36
 
36
37
  // Convert React props to web component attributes
37
38
  const webComponentProps: Record<string, any> = {
38
- ...props,
39
+ ...hostProps(props),
39
40
  ref: elementRef,
40
41
  id,
41
42
  };
@@ -1,7 +1,16 @@
1
1
  import React, { useEffect, useRef, useImperativeHandle } from 'react';
2
+ import { hostProps } from '../utils/host-props';
2
3
  import { needsPropertyBridge } from '../utils/react-version';
3
4
  import { useBooleanProperty, coerceBool } from '../utils/use-boolean-prop';
4
5
 
6
+ /** Detail payload for nearstart / nearend events. */
7
+ export interface TyScrollNearEdgeDetail {
8
+ distance: number;
9
+ scrollTop: number;
10
+ scrollHeight: number;
11
+ clientHeight: number;
12
+ }
13
+
5
14
  // Type definitions for Ty ScrollContainer component
6
15
  export interface TyScrollContainerProps extends React.HTMLAttributes<HTMLElement> {
7
16
  /** Maximum height of the scroll container */
@@ -10,9 +19,27 @@ export interface TyScrollContainerProps extends React.HTMLAttributes<HTMLElement
10
19
  /** Enable/disable scroll shadows (default: true) */
11
20
  shadow?: boolean;
12
21
 
13
- /** Hide native scrollbar */
22
+ /** Hide native scrollbar (no custom scrollbar) */
14
23
  hideScrollbar?: boolean;
15
24
 
25
+ /** Use the styled custom scrollbar */
26
+ customScrollbar?: boolean;
27
+
28
+ /** Enable horizontal scrolling */
29
+ overflowX?: boolean;
30
+
31
+ /** Preserve visual position when content is prepended above the viewport */
32
+ scrollAnchoring?: boolean;
33
+
34
+ /** Distance (px) from an edge at which nearstart/nearend fire (default 100) */
35
+ nearEdgeThreshold?: number;
36
+
37
+ /** Fired once when scrolled within the threshold of the bottom */
38
+ onNearEnd?: (event: CustomEvent<TyScrollNearEdgeDetail>) => void;
39
+
40
+ /** Fired once when scrolled within the threshold of the top */
41
+ onNearStart?: (event: CustomEvent<TyScrollNearEdgeDetail>) => void;
42
+
16
43
  /** Content to scroll */
17
44
  children?: React.ReactNode;
18
45
  }
@@ -25,6 +52,12 @@ export interface TyScrollContainerRef {
25
52
  scrollToTop: (smooth?: boolean) => void;
26
53
  /** Scroll to bottom */
27
54
  scrollToBottom: (smooth?: boolean) => void;
55
+ /** Scroll to far left */
56
+ scrollToLeft: (smooth?: boolean) => void;
57
+ /** Scroll to far right */
58
+ scrollToRight: (smooth?: boolean) => void;
59
+ /** Scroll a descendant (element or CSS selector) into view */
60
+ scrollToElement: (target: Element | string, smooth?: boolean) => void;
28
61
  /** Get the underlying scroll element */
29
62
  scrollElement: HTMLElement | null;
30
63
  /** Get the native element */
@@ -38,35 +71,29 @@ export const TyScrollContainer = React.forwardRef<TyScrollContainerRef, TyScroll
38
71
  maxHeight,
39
72
  shadow,
40
73
  hideScrollbar,
74
+ customScrollbar,
75
+ overflowX,
76
+ scrollAnchoring,
77
+ nearEdgeThreshold,
78
+ onNearEnd,
79
+ onNearStart,
41
80
  ...props
42
81
  }, ref) => {
43
82
  const elementRef = useRef<HTMLElement>(null);
44
83
 
45
84
  // Expose imperative methods via ref
46
85
  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
- }
86
+ updateShadows: () => { (elementRef.current as any)?.updateShadows?.(); },
87
+ scrollToTop: (smooth = true) => { (elementRef.current as any)?.scrollToTop?.(smooth); },
88
+ scrollToBottom: (smooth = true) => { (elementRef.current as any)?.scrollToBottom?.(smooth); },
89
+ scrollToLeft: (smooth = true) => { (elementRef.current as any)?.scrollToLeft?.(smooth); },
90
+ scrollToRight: (smooth = true) => { (elementRef.current as any)?.scrollToRight?.(smooth); },
91
+ scrollToElement: (target, smooth = true) => { (elementRef.current as any)?.scrollToElement?.(target, smooth); },
92
+ get scrollElement() { return (elementRef.current as any)?.scrollElement ?? null; },
93
+ get element() { return elementRef.current; }
66
94
  }), []);
67
95
 
68
- // shadow defaults to true; only the explicit-false case matters at the
69
- // attribute level. Bridge it imperatively so flipping back to true
96
+ // shadow defaults to true; bridge it imperatively so flipping back to true
70
97
  // propagates on React 18.
71
98
  useEffect(() => {
72
99
  if (!needsPropertyBridge) return;
@@ -76,20 +103,44 @@ export const TyScrollContainer = React.forwardRef<TyScrollContainerRef, TyScroll
76
103
  const next = coerceBool(shadow);
77
104
  if (Boolean(el.shadow) !== next) el.shadow = next;
78
105
  }, [shadow]);
106
+
79
107
  const isHideScrollbar = useBooleanProperty(elementRef, 'hideScrollbar', hideScrollbar);
108
+ const isCustomScrollbar = useBooleanProperty(elementRef, 'customScrollbar', customScrollbar);
109
+ const isOverflowX = useBooleanProperty(elementRef, 'overflowX', overflowX);
110
+ const isScrollAnchoring = useBooleanProperty(elementRef, 'scrollAnchoring', scrollAnchoring);
111
+
112
+ // Custom events → React callbacks
113
+ useEffect(() => {
114
+ const el = elementRef.current;
115
+ if (!el) return;
116
+ const bound: Array<[string, EventListener]> = [];
117
+ if (onNearEnd) {
118
+ const h = (e: Event) => onNearEnd(e as CustomEvent<TyScrollNearEdgeDetail>);
119
+ el.addEventListener('nearend', h); bound.push(['nearend', h]);
120
+ }
121
+ if (onNearStart) {
122
+ const h = (e: Event) => onNearStart(e as CustomEvent<TyScrollNearEdgeDetail>);
123
+ el.addEventListener('nearstart', h); bound.push(['nearstart', h]);
124
+ }
125
+ return () => bound.forEach(([n, h]) => el.removeEventListener(n, h));
126
+ }, [onNearEnd, onNearStart]);
80
127
 
81
128
  // Convert React props to web component attributes
82
129
  const webComponentProps: Record<string, any> = {
83
- ...props,
130
+ ...hostProps(props),
84
131
  ref: elementRef,
85
132
  };
86
133
 
87
- // Add string attributes
134
+ // String attributes
88
135
  if (maxHeight) webComponentProps['max-height'] = maxHeight;
136
+ if (nearEdgeThreshold != null) webComponentProps['near-edge-threshold'] = String(nearEdgeThreshold);
89
137
 
90
- // Add boolean attributes
138
+ // Boolean attributes
91
139
  if (shadow !== undefined && !coerceBool(shadow)) webComponentProps.shadow = 'false';
92
140
  if (isHideScrollbar) webComponentProps['hide-scrollbar'] = '';
141
+ if (isCustomScrollbar) webComponentProps['custom-scrollbar'] = '';
142
+ if (isOverflowX) webComponentProps['overflow-x'] = '';
143
+ if (isScrollAnchoring) webComponentProps['scroll-anchoring'] = '';
93
144
 
94
145
  return React.createElement(
95
146
  'ty-scroll-container',