tyrell-react 1.0.0-TC29 → 1.0.0-TC31

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 (122) hide show
  1. package/dist/components/TyButton.d.ts.map +1 -1
  2. package/dist/components/TyButton.js +2 -1
  3. package/dist/components/TyButton.js.map +1 -1
  4. package/dist/components/TyCalendar.d.ts +5 -0
  5. package/dist/components/TyCalendar.d.ts.map +1 -1
  6. package/dist/components/TyCalendar.js +6 -2
  7. package/dist/components/TyCalendar.js.map +1 -1
  8. package/dist/components/TyCalendarMonth.d.ts +5 -0
  9. package/dist/components/TyCalendarMonth.d.ts.map +1 -1
  10. package/dist/components/TyCalendarMonth.js +5 -2
  11. package/dist/components/TyCalendarMonth.js.map +1 -1
  12. package/dist/components/TyCalendarNavigation.d.ts.map +1 -1
  13. package/dist/components/TyCalendarNavigation.js +2 -1
  14. package/dist/components/TyCalendarNavigation.js.map +1 -1
  15. package/dist/components/TyCheckbox.d.ts +4 -1
  16. package/dist/components/TyCheckbox.d.ts.map +1 -1
  17. package/dist/components/TyCheckbox.js +2 -1
  18. package/dist/components/TyCheckbox.js.map +1 -1
  19. package/dist/components/TyCopy.d.ts +4 -1
  20. package/dist/components/TyCopy.d.ts.map +1 -1
  21. package/dist/components/TyCopy.js +2 -1
  22. package/dist/components/TyCopy.js.map +1 -1
  23. package/dist/components/TyDatePicker.d.ts +6 -3
  24. package/dist/components/TyDatePicker.d.ts.map +1 -1
  25. package/dist/components/TyDatePicker.js +2 -1
  26. package/dist/components/TyDatePicker.js.map +1 -1
  27. package/dist/components/TyFileUpload.d.ts.map +1 -1
  28. package/dist/components/TyFileUpload.js +2 -1
  29. package/dist/components/TyFileUpload.js.map +1 -1
  30. package/dist/components/TyInput.d.ts +6 -2
  31. package/dist/components/TyInput.d.ts.map +1 -1
  32. package/dist/components/TyInput.js +2 -1
  33. package/dist/components/TyInput.js.map +1 -1
  34. package/dist/components/TyModal.d.ts +5 -0
  35. package/dist/components/TyModal.d.ts.map +1 -1
  36. package/dist/components/TyModal.js +5 -2
  37. package/dist/components/TyModal.js.map +1 -1
  38. package/dist/components/TyOption.d.ts.map +1 -1
  39. package/dist/components/TyOption.js +2 -1
  40. package/dist/components/TyOption.js.map +1 -1
  41. package/dist/components/TyPopup.d.ts.map +1 -1
  42. package/dist/components/TyPopup.js +2 -1
  43. package/dist/components/TyPopup.js.map +1 -1
  44. package/dist/components/TyRadio.d.ts +4 -1
  45. package/dist/components/TyRadio.d.ts.map +1 -1
  46. package/dist/components/TyRadio.js +2 -1
  47. package/dist/components/TyRadio.js.map +1 -1
  48. package/dist/components/TyRadioGroup.d.ts +4 -1
  49. package/dist/components/TyRadioGroup.d.ts.map +1 -1
  50. package/dist/components/TyRadioGroup.js +2 -1
  51. package/dist/components/TyRadioGroup.js.map +1 -1
  52. package/dist/components/TyResizeObserver.d.ts.map +1 -1
  53. package/dist/components/TyResizeObserver.js +2 -1
  54. package/dist/components/TyResizeObserver.js.map +1 -1
  55. package/dist/components/TyScrollContainer.d.ts.map +1 -1
  56. package/dist/components/TyScrollContainer.js +2 -1
  57. package/dist/components/TyScrollContainer.js.map +1 -1
  58. package/dist/components/TySelect.d.ts +28 -0
  59. package/dist/components/TySelect.d.ts.map +1 -1
  60. package/dist/components/TySelect.js +20 -4
  61. package/dist/components/TySelect.js.map +1 -1
  62. package/dist/components/TyStep.d.ts.map +1 -1
  63. package/dist/components/TyStep.js +2 -1
  64. package/dist/components/TyStep.js.map +1 -1
  65. package/dist/components/TySwitch.d.ts +4 -1
  66. package/dist/components/TySwitch.d.ts.map +1 -1
  67. package/dist/components/TySwitch.js +2 -1
  68. package/dist/components/TySwitch.js.map +1 -1
  69. package/dist/components/TyTab.d.ts.map +1 -1
  70. package/dist/components/TyTab.js +2 -1
  71. package/dist/components/TyTab.js.map +1 -1
  72. package/dist/components/TyTabs.d.ts.map +1 -1
  73. package/dist/components/TyTabs.js +2 -1
  74. package/dist/components/TyTabs.js.map +1 -1
  75. package/dist/components/TyTag.d.ts +4 -1
  76. package/dist/components/TyTag.d.ts.map +1 -1
  77. package/dist/components/TyTag.js +2 -1
  78. package/dist/components/TyTag.js.map +1 -1
  79. package/dist/components/TyTextarea.d.ts.map +1 -1
  80. package/dist/components/TyTextarea.js +2 -1
  81. package/dist/components/TyTextarea.js.map +1 -1
  82. package/dist/components/TyTooltip.d.ts +4 -1
  83. package/dist/components/TyTooltip.d.ts.map +1 -1
  84. package/dist/components/TyTooltip.js +2 -1
  85. package/dist/components/TyTooltip.js.map +1 -1
  86. package/dist/components/TyWizard.d.ts.map +1 -1
  87. package/dist/components/TyWizard.js +2 -1
  88. package/dist/components/TyWizard.js.map +1 -1
  89. package/dist/utils/host-props.d.ts +11 -0
  90. package/dist/utils/host-props.d.ts.map +1 -0
  91. package/dist/utils/host-props.js +16 -0
  92. package/dist/utils/host-props.js.map +1 -0
  93. package/dist/version.d.ts +1 -1
  94. package/dist/version.js +1 -1
  95. package/package.json +1 -1
  96. package/src/components/TyButton.tsx +2 -1
  97. package/src/components/TyCalendar.tsx +14 -2
  98. package/src/components/TyCalendarMonth.tsx +11 -2
  99. package/src/components/TyCalendarNavigation.tsx +2 -1
  100. package/src/components/TyCheckbox.tsx +6 -2
  101. package/src/components/TyCopy.tsx +6 -2
  102. package/src/components/TyDatePicker.tsx +8 -4
  103. package/src/components/TyFileUpload.tsx +2 -1
  104. package/src/components/TyInput.tsx +8 -3
  105. package/src/components/TyModal.tsx +10 -1
  106. package/src/components/TyOption.tsx +2 -1
  107. package/src/components/TyPopup.tsx +2 -1
  108. package/src/components/TyRadio.tsx +6 -2
  109. package/src/components/TyRadioGroup.tsx +6 -2
  110. package/src/components/TyResizeObserver.tsx +2 -1
  111. package/src/components/TyScrollContainer.tsx +2 -1
  112. package/src/components/TySelect.tsx +46 -3
  113. package/src/components/TyStep.tsx +2 -1
  114. package/src/components/TySwitch.tsx +6 -2
  115. package/src/components/TyTab.tsx +2 -1
  116. package/src/components/TyTabs.tsx +2 -1
  117. package/src/components/TyTag.tsx +6 -2
  118. package/src/components/TyTextarea.tsx +2 -1
  119. package/src/components/TyTooltip.tsx +6 -2
  120. package/src/components/TyWizard.tsx +2 -1
  121. package/src/utils/host-props.ts +14 -0
  122. package/src/version.ts +1 -1
@@ -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,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 Option component
@@ -44,7 +45,7 @@ export const TyOption = React.forwardRef<HTMLElement, TyOptionProps>(
44
45
  return React.createElement(
45
46
  'ty-option',
46
47
  {
47
- ...props,
48
+ ...hostProps(props),
48
49
  ...(label && { label }),
49
50
  ...(flavor && { flavor }),
50
51
  ...(isDisabled && { disabled: "" }),
@@ -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,4 +1,5 @@
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
 
@@ -126,7 +127,7 @@ export const TyScrollContainer = React.forwardRef<TyScrollContainerRef, TyScroll
126
127
 
127
128
  // Convert React props to web component attributes
128
129
  const webComponentProps: Record<string, any> = {
129
- ...props,
130
+ ...hostProps(props),
130
131
  ref: elementRef,
131
132
  };
132
133
 
@@ -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 Select component
6
10
  export interface TySelectItem {
7
11
  value: string;
@@ -67,12 +71,31 @@ export interface TySelectProps extends Omit<React.HTMLAttributes<HTMLElement>, '
67
71
  /** Debounce for the search event in ms (0-5000) */
68
72
  debounce?: number;
69
73
 
74
+ /**
75
+ * Enter on unmatched search text mints a new <ty-option> (forces the
76
+ * search row on, same as externalSearch). Listen to onCreate to mutate
77
+ * the value before it's created, or preventDefault() to take over
78
+ * entirely (e.g. after a server round-trip).
79
+ */
80
+ allowCreate?: boolean;
81
+
82
+ /**
83
+ * Opt-in normalizer for the value allowCreate mints — the display label
84
+ * is always kept verbatim. 'slug': lowercase, spaces → '_', strips
85
+ * everything else non-alphanumeric. Default: value === typed text.
86
+ */
87
+ createTransform?: 'slug';
88
+
70
89
  /** Loading state — shows a spinner in the options area (external search in flight) */
71
90
  loading?: boolean;
72
91
 
73
92
  /** Size variant */
93
+ /** Field size — fields come in exactly three; legacy xs/xl map to sm/lg */
74
94
  size?: 'sm' | 'md' | 'lg';
75
95
 
96
+ /** Visual flavor for the field border — built-in semantic, +/- shade, or a custom flavor backed by --ty-*-X tokens */
97
+ flavor?: ShadedFlavor | (string & {});
98
+
76
99
  /** Callback when selection changes */
77
100
  onChange?: (event: CustomEvent<TySelectEventDetail>) => void;
78
101
 
@@ -81,6 +104,13 @@ export interface TySelectProps extends Omit<React.HTMLAttributes<HTMLElement>, '
81
104
  onOpen?: (event: CustomEvent) => void;
82
105
  onClose?: (event: CustomEvent) => void;
83
106
 
107
+ /**
108
+ * Fired before allowCreate mints a new option — cancelable. Mutate
109
+ * `event.detail.value` to change the id that gets created (e.g. slugify
110
+ * it yourself), or call `event.preventDefault()` to create it yourself.
111
+ */
112
+ onCreate?: (event: CustomEvent<{ value: string; label: string }>) => void;
113
+
84
114
  /** TyOption children (plus optional slot="trigger" / slot="start" / slot="end" elements) */
85
115
  children?: React.ReactNode;
86
116
  }
@@ -100,12 +130,16 @@ export const TySelect = React.forwardRef<HTMLElement, TySelectProps>(
100
130
  searchable,
101
131
  externalSearch,
102
132
  debounce,
133
+ allowCreate,
134
+ createTransform,
103
135
  loading,
104
136
  size,
137
+ flavor,
105
138
  onChange,
106
139
  onSearch,
107
140
  onOpen,
108
141
  onClose,
142
+ onCreate,
109
143
  children,
110
144
  ...props
111
145
  }, ref) => {
@@ -145,6 +179,9 @@ export const TySelect = React.forwardRef<HTMLElement, TySelectProps>(
145
179
 
146
180
  const handleOpen = useCallback((event: Event) => { if (onOpen) onOpen(event as CustomEvent); }, [onOpen]);
147
181
  const handleClose = useCallback((event: Event) => { if (onClose) onClose(event as CustomEvent); }, [onClose]);
182
+ const handleCreate = useCallback((event: Event) => {
183
+ if (onCreate) onCreate(event as CustomEvent<{ value: string; label: string }>);
184
+ }, [onCreate]);
148
185
 
149
186
  // Set up event listeners
150
187
  useEffect(() => {
@@ -155,14 +192,16 @@ export const TySelect = React.forwardRef<HTMLElement, TySelectProps>(
155
192
  if (onSearch) element.addEventListener('search', handleSearch);
156
193
  if (onOpen) element.addEventListener('open', handleOpen);
157
194
  if (onClose) element.addEventListener('close', handleClose);
195
+ if (onCreate) element.addEventListener('create', handleCreate);
158
196
 
159
197
  return () => {
160
198
  if (onChange) element.removeEventListener('change', handleChange);
161
199
  if (onSearch) element.removeEventListener('search', handleSearch);
162
200
  if (onOpen) element.removeEventListener('open', handleOpen);
163
201
  if (onClose) element.removeEventListener('close', handleClose);
202
+ if (onCreate) element.removeEventListener('create', handleCreate);
164
203
  };
165
- }, [handleChange, handleSearch, handleOpen, handleClose, onChange, onSearch, onOpen, onClose]);
204
+ }, [handleChange, handleSearch, handleOpen, handleClose, handleCreate, onChange, onSearch, onOpen, onClose, onCreate]);
166
205
 
167
206
  // Imperative property sync for boolean props (see use-boolean-prop.ts).
168
207
  const isMultiple = useBooleanProperty(elementRef, 'multiple', multiple);
@@ -172,10 +211,11 @@ export const TySelect = React.forwardRef<HTMLElement, TySelectProps>(
172
211
  const isRequired = useBooleanProperty(elementRef, 'required', required);
173
212
  const isLoading = useBooleanProperty(elementRef, 'loading', loading);
174
213
  const isExternalSearch = useBooleanProperty(elementRef, 'externalSearch', externalSearch);
214
+ const isAllowCreate = useBooleanProperty(elementRef, 'allowCreate', allowCreate);
175
215
 
176
216
  // Convert React props to web component attributes
177
217
  const webComponentProps: Record<string, any> = {
178
- ...props,
218
+ ...hostProps(props),
179
219
  ref: elementRef,
180
220
  };
181
221
 
@@ -191,12 +231,15 @@ export const TySelect = React.forwardRef<HTMLElement, TySelectProps>(
191
231
  if (isRequired) webComponentProps.required = '';
192
232
  if (isLoading) webComponentProps.loading = '';
193
233
  if (isExternalSearch) webComponentProps['external-search'] = '';
234
+ if (isAllowCreate) webComponentProps['allow-create'] = '';
194
235
 
195
236
  if (placeholder) webComponentProps.placeholder = placeholder;
196
237
  if (label) webComponentProps.label = label;
197
238
  if (name) webComponentProps.name = name;
198
239
  if (size) webComponentProps.size = size;
240
+ if (flavor) webComponentProps.flavor = flavor;
199
241
  if (debounce !== undefined) webComponentProps.debounce = debounce.toString();
242
+ if (createTransform) webComponentProps['create-transform'] = createTransform;
200
243
  if (searchable === 'always' || searchable === true) webComponentProps.searchable = 'true';
201
244
  else if (searchable === 'never' || searchable === false) webComponentProps.searchable = 'false';
202
245
  // 'auto' / undefined → omit (component default)
@@ -230,7 +273,7 @@ export const TySelectedTags = React.forwardRef<HTMLElement, TySelectedTagsProps>
230
273
  }
231
274
  }, [ref]);
232
275
 
233
- const webComponentProps: Record<string, any> = { ...props, ref: elementRef };
276
+ const webComponentProps: Record<string, any> = { ...hostProps(props), ref: elementRef };
234
277
  if (htmlFor) webComponentProps.for = htmlFor;
235
278
 
236
279
  return React.createElement('ty-selected-tags', webComponentProps, children);
@@ -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 Step component
4
5
  export interface TyStepProps extends React.HTMLAttributes<HTMLElement> {
@@ -47,7 +48,7 @@ export const TyStep = React.forwardRef<HTMLElement, TyStepProps>(
47
48
 
48
49
  // Convert React props to web component attributes
49
50
  const webComponentProps: Record<string, any> = {
50
- ...props,
51
+ ...hostProps(props),
51
52
  ref: elementRef,
52
53
  id,
53
54
  };
@@ -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 TySwitchProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'onInput'> {
5
9
  /** Checked (on) state */
6
10
  checked?: boolean;
@@ -21,7 +25,7 @@ export interface TySwitchProps extends Omit<React.HTMLAttributes<HTMLElement>, '
21
25
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
22
26
 
23
27
  /** Semantic styling variant */
24
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
28
+ flavor?: ShadedFlavor | (string & {});
25
29
 
26
30
  /**
27
31
  * Fires when switch state changes (React convention)
@@ -94,7 +98,7 @@ export const TySwitch = React.forwardRef<HTMLElement, TySwitchProps>(
94
98
  const isRequired = useBooleanProperty(elementRef, 'required', required);
95
99
 
96
100
  const webComponentProps: Record<string, any> = {
97
- ...props,
101
+ ...hostProps(props),
98
102
  ref: elementRef,
99
103
  };
100
104
 
@@ -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 Tab component
@@ -40,7 +41,7 @@ export const TyTab = React.forwardRef<HTMLElement, TyTabProps>(
40
41
 
41
42
  // Convert React props to web component attributes
42
43
  const webComponentProps: Record<string, any> = {
43
- ...props,
44
+ ...hostProps(props),
44
45
  ref: elementRef,
45
46
  };
46
47
 
@@ -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 Tabs component
4
5
  export interface TyTabsProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
@@ -72,7 +73,7 @@ export const TyTabs = React.forwardRef<HTMLElement, TyTabsProps>(
72
73
 
73
74
  // Convert React props to web component attributes
74
75
  const webComponentProps: Record<string, any> = {
75
- ...props,
76
+ ...hostProps(props),
76
77
  ref: elementRef,
77
78
  };
78
79
 
@@ -1,6 +1,10 @@
1
1
  import React, { useEffect, useRef, useCallback } 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
  // CSS custom properties that cascade into the shadow DOM for full color control
5
9
  export interface TyTagCSSProperties extends React.CSSProperties {
6
10
  '--tag-bg'?: string;
@@ -10,7 +14,7 @@ export interface TyTagCSSProperties extends React.CSSProperties {
10
14
 
11
15
  // Type definitions for Ty Tag component
12
16
  export interface TyTagProps extends Omit<React.HTMLAttributes<HTMLElement>, 'style' | 'onClick'> {
13
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
17
+ flavor?: ShadedFlavor | (string & {});
14
18
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
15
19
  notPill?: boolean;
16
20
  clickable?: boolean;
@@ -67,7 +71,7 @@ export const TyTag = React.forwardRef<HTMLElement, TyTagProps>(
67
71
  return React.createElement(
68
72
  'ty-tag',
69
73
  {
70
- ...props,
74
+ ...hostProps(props),
71
75
  // click is dispatched as composed CustomEvent by the web component — React's
72
76
  // synthetic onClick already catches it, so we just pass it through as onClick
73
77
  ...(onClick && { onClick }),
@@ -1,4 +1,5 @@
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
 
@@ -167,7 +168,7 @@ export const TyTextarea = React.forwardRef<HTMLElement, TyTextareaProps>(
167
168
  return React.createElement(
168
169
  'ty-textarea',
169
170
  {
170
- ...props,
171
+ ...hostProps(props),
171
172
  ...(isDisabled && { disabled: "" }),
172
173
  ...(isRequired && { required: "" }),
173
174
  ...(minHeight && { 'min-height': minHeight }), // Convert camelCase to kebab-case
@@ -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 Tooltip component
5
9
  export interface TyTooltipProps extends React.HTMLAttributes<HTMLElement> {
6
10
  /** Tooltip positioning relative to the parent element */
@@ -16,7 +20,7 @@ export interface TyTooltipProps extends React.HTMLAttributes<HTMLElement> {
16
20
  disabled?: boolean;
17
21
 
18
22
  /** Semantic styling variant */
19
- flavor?: 'dark' | 'light' | 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'neutral';
23
+ flavor?: 'dark' | 'light' | 'info' | ShadedFlavor | (string & {});
20
24
 
21
25
  /** Tooltip content */
22
26
  children?: React.ReactNode;
@@ -48,7 +52,7 @@ export const TyTooltip = React.forwardRef<HTMLElement, TyTooltipProps>(
48
52
 
49
53
  // Convert React props to web component attributes
50
54
  const webComponentProps: Record<string, any> = {
51
- ...props,
55
+ ...hostProps(props),
52
56
  ref: elementRef,
53
57
  };
54
58
 
@@ -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 Wizard component
4
5
  export interface TyWizardProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange'> {
@@ -77,7 +78,7 @@ export const TyWizard = React.forwardRef<HTMLElement, TyWizardProps>(
77
78
 
78
79
  // Convert React props to web component attributes
79
80
  const webComponentProps: Record<string, any> = {
80
- ...props,
81
+ ...hostProps(props),
81
82
  ref: elementRef,
82
83
  };
83
84
 
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Normalize pass-through props for a custom-element host.
3
+ *
4
+ * React 18 does not special-case `className` on custom elements — it
5
+ * lowercases the prop into a literal `classname=""` attribute, which no CSS
6
+ * ever matches. Renaming it to `class` makes React pass it through verbatim,
7
+ * so host-level utility classes (layout, positioning) actually apply.
8
+ * React 19 handles `className` natively; this stays harmless there.
9
+ */
10
+ export function hostProps<T extends Record<string, any>>(props: T): Record<string, any> {
11
+ const { className, ...rest } = props;
12
+ if (className != null) (rest as Record<string, any>).class = className;
13
+ return rest;
14
+ }
package/src/version.ts CHANGED
@@ -3,4 +3,4 @@
3
3
  // Run 'npm run generate:version' to regenerate.
4
4
 
5
5
  /** Current version of tyrell-react. Synced with package.json on build. */
6
- export const VERSION = '1.0.0-TC29'
6
+ export const VERSION = '1.0.0-TC31'