tyrell-react 1.0.0-RC10 → 1.0.0-RC12

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 (65) hide show
  1. package/dist/components/TyCalendar.d.ts +6 -0
  2. package/dist/components/TyCalendar.d.ts.map +1 -1
  3. package/dist/components/TyCalendar.js +16 -2
  4. package/dist/components/TyCalendar.js.map +1 -1
  5. package/dist/components/TyCalendarMonth.d.ts +4 -0
  6. package/dist/components/TyCalendarMonth.d.ts.map +1 -1
  7. package/dist/components/TyCalendarMonth.js +5 -1
  8. package/dist/components/TyCalendarMonth.js.map +1 -1
  9. package/dist/components/TyCalendarNavigation.d.ts +4 -0
  10. package/dist/components/TyCalendarNavigation.d.ts.map +1 -1
  11. package/dist/components/TyCalendarNavigation.js +5 -1
  12. package/dist/components/TyCalendarNavigation.js.map +1 -1
  13. package/dist/components/TyCheckbox.d.ts +2 -0
  14. package/dist/components/TyCheckbox.d.ts.map +1 -1
  15. package/dist/components/TyCheckbox.js +4 -1
  16. package/dist/components/TyCheckbox.js.map +1 -1
  17. package/dist/components/TyCopy.d.ts +4 -0
  18. package/dist/components/TyCopy.d.ts.map +1 -1
  19. package/dist/components/TyCopy.js +19 -1
  20. package/dist/components/TyCopy.js.map +1 -1
  21. package/dist/components/TyDatePicker.d.ts +4 -0
  22. package/dist/components/TyDatePicker.d.ts.map +1 -1
  23. package/dist/components/TyDatePicker.js +7 -1
  24. package/dist/components/TyDatePicker.js.map +1 -1
  25. package/dist/components/TyOption.d.ts +8 -0
  26. package/dist/components/TyOption.d.ts.map +1 -1
  27. package/dist/components/TyOption.js +3 -1
  28. package/dist/components/TyOption.js.map +1 -1
  29. package/dist/components/TyScrollContainer.d.ts +26 -1
  30. package/dist/components/TyScrollContainer.d.ts.map +1 -1
  31. package/dist/components/TyScrollContainer.js +41 -24
  32. package/dist/components/TyScrollContainer.js.map +1 -1
  33. package/dist/components/TySelect.d.ts +75 -0
  34. package/dist/components/TySelect.d.ts.map +1 -0
  35. package/dist/components/TySelect.js +134 -0
  36. package/dist/components/TySelect.js.map +1 -0
  37. package/dist/components/index.d.ts +6 -8
  38. package/dist/components/index.d.ts.map +1 -1
  39. package/dist/components/index.js +5 -4
  40. package/dist/components/index.js.map +1 -1
  41. package/dist/version.d.ts +1 -1
  42. package/dist/version.js +1 -1
  43. package/package.json +1 -1
  44. package/src/components/TyCalendar.tsx +30 -1
  45. package/src/components/TyCalendarMonth.tsx +11 -1
  46. package/src/components/TyCalendarNavigation.tsx +11 -1
  47. package/src/components/TyCheckbox.tsx +8 -2
  48. package/src/components/TyCopy.tsx +25 -1
  49. package/src/components/TyDatePicker.tsx +16 -0
  50. package/src/components/TyOption.tsx +14 -1
  51. package/src/components/TyScrollContainer.tsx +74 -24
  52. package/src/components/TySelect.tsx +240 -0
  53. package/src/components/index.ts +8 -13
  54. package/src/version.ts +1 -1
  55. package/dist/components/TyDropdown.d.ts +0 -62
  56. package/dist/components/TyDropdown.d.ts.map +0 -1
  57. package/dist/components/TyDropdown.js +0 -124
  58. package/dist/components/TyDropdown.js.map +0 -1
  59. package/dist/components/TyMultiselect.d.ts +0 -57
  60. package/dist/components/TyMultiselect.d.ts.map +0 -1
  61. package/dist/components/TyMultiselect.js +0 -111
  62. package/dist/components/TyMultiselect.js.map +0 -1
  63. package/src/components/EventConventionTest.tsx +0 -155
  64. package/src/components/TyDropdown.tsx +0 -240
  65. package/src/components/TyMultiselect.tsx +0 -208
@@ -1,240 +0,0 @@
1
- import React, { useEffect, useRef, useCallback } from 'react';
2
- import { needsPropertyBridge } from '../utils/react-version';
3
- import { useBooleanProperty, coerceBool } from '../utils/use-boolean-prop';
4
-
5
- // Option data structure for data-driven approach
6
- export interface OptionData {
7
- value: string;
8
- text: string;
9
- disabled?: boolean;
10
- }
11
-
12
- // Event detail structure for dropdown events
13
- export interface TyDropdownEventDetail {
14
- option: HTMLElement;
15
- }
16
-
17
- // Type definitions for Ty Dropdown component
18
- export interface TyDropdownProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'style'> {
19
- style?: import('./TyInput').TyInputCSSProperties;
20
- /** Semantic styling variant */
21
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
22
-
23
- /** Dropdown size */
24
- size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
25
-
26
- /** Selected value */
27
- value?: string;
28
-
29
- /** Placeholder text */
30
- placeholder?: string;
31
-
32
- /** Dropdown label */
33
- label?: string;
34
-
35
- /** Disable the dropdown */
36
- disabled?: boolean;
37
-
38
- /**
39
- * Loading state — replaces the open popup options list with a centered
40
- * spinner. Search input stays usable. Pair with `externalSearch` while
41
- * fetching results from a parent-owned data source.
42
- */
43
- loading?: boolean;
44
-
45
- /** Make dropdown readonly */
46
- readonly?: boolean;
47
-
48
- /** Required field */
49
- required?: boolean;
50
-
51
- /** Show clear button */
52
- clearable?: boolean;
53
-
54
- /** Disable clear button (alias for clearable={false}) */
55
- notClearable?: boolean;
56
-
57
- /** Debounce in milliseconds (0-5000) */
58
- debounce?: number;
59
-
60
- /**
61
- * Switch to external (remote) search mode. Default is `false` — the dropdown
62
- * filters its options locally. When `true`, the dropdown stops filtering and
63
- * dispatches `search` events on each keystroke; the parent owns filtering
64
- * and updates the children.
65
- */
66
- externalSearch?: boolean;
67
-
68
- /** @deprecated Use `externalSearch` instead. */
69
- notSearchable?: boolean;
70
-
71
- /** @deprecated Use `externalSearch` instead. Pass `searchable={false}` was equivalent to `externalSearch={true}`. */
72
- searchable?: boolean;
73
-
74
- /** Form field name for form submission */
75
- name?: string;
76
-
77
- // Data-driven approach
78
- options?: OptionData[];
79
-
80
- // React event handlers
81
- onChange?: (event: CustomEvent<TyDropdownEventDetail>) => void;
82
- onSearch?: (event: CustomEvent<{ query: string; element: HTMLElement }>) => void;
83
-
84
- // Children for slotted approach
85
- children?: React.ReactNode;
86
- }
87
-
88
- // React wrapper for ty-dropdown web component
89
- export const TyDropdown = React.forwardRef<HTMLElement, TyDropdownProps>(
90
- ({
91
- options,
92
- children,
93
- onChange,
94
- onSearch,
95
- disabled,
96
- loading,
97
- externalSearch,
98
- notSearchable,
99
- searchable,
100
- clearable,
101
- notClearable,
102
- debounce,
103
- name,
104
- value,
105
- ...props
106
- }, ref) => {
107
- const elementRef = useRef<HTMLElement>(null);
108
-
109
- const handleChange = useCallback((event: CustomEvent<TyDropdownEventDetail>) => {
110
- if (onChange) {
111
- onChange(event);
112
- }
113
- }, [onChange]);
114
-
115
- const handleSearch = useCallback((event: CustomEvent<{ query: string; element: HTMLElement }>) => {
116
- if (onSearch) {
117
- onSearch(event);
118
- }
119
- }, [onSearch]);
120
-
121
- useEffect(() => {
122
- const element = elementRef.current;
123
- if (!element) return;
124
-
125
- // Listen for custom events from ty-dropdown
126
- if (onChange) {
127
- element.addEventListener('change', handleChange as EventListener);
128
- }
129
-
130
- if (onSearch) {
131
- element.addEventListener('search', handleSearch as EventListener);
132
- }
133
-
134
- return () => {
135
- if (onChange) {
136
- element.removeEventListener('change', handleChange as EventListener);
137
- }
138
- if (onSearch) {
139
- element.removeEventListener('search', handleSearch as EventListener);
140
- }
141
- };
142
- }, [handleChange, handleSearch, onChange, onSearch]);
143
-
144
- // Imperatively sync `value` to the underlying element's property whenever
145
- // it changes. React 18's prop-to-property bridging for custom elements is
146
- // unreliable for empty strings (programmatic resets), so we set the
147
- // property directly to guarantee the dropdown clears on `value=""`.
148
- // React 19+ handles this natively, so the effect short-circuits there.
149
- useEffect(() => {
150
- if (!needsPropertyBridge) return;
151
- const element = elementRef.current as any;
152
- if (!element) return;
153
- if (element.value !== value) {
154
- element.value = value ?? '';
155
- }
156
- }, [value]);
157
-
158
- // Handle ref forwarding
159
- useEffect(() => {
160
- if (ref && elementRef.current) {
161
- if (typeof ref === 'function') {
162
- ref(elementRef.current);
163
- } else {
164
- ref.current = elementRef.current;
165
- }
166
- }
167
- }, [ref]);
168
-
169
- // Render options from data if provided, otherwise use children
170
- const renderContent = () => {
171
- if (options && options.length > 0) {
172
- // Data-driven approach - create ty-option elements
173
- return options.map((option, index) =>
174
- React.createElement(
175
- 'ty-option',
176
- {
177
- key: option.value || index,
178
- value: option.value,
179
- ...(option.disabled && { disabled: "" }),
180
- },
181
- option.text
182
- )
183
- );
184
- }
185
-
186
- // Slotted approach - use provided children
187
- return children;
188
- };
189
-
190
- // Imperative property sync for boolean props (see use-boolean-prop.ts).
191
- const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
192
- const isLoading = useBooleanProperty(elementRef, 'loading', loading);
193
- const isExternalSearch = coerceBool(externalSearch) || coerceBool(notSearchable) || searchable === false;
194
- useBooleanProperty(elementRef, 'externalSearch', isExternalSearch);
195
- // clearable: explicit boolean OR the `notClearable` alias inverts it
196
- const isClearable = clearable !== undefined
197
- ? coerceBool(clearable)
198
- : (coerceBool(notClearable) ? false : undefined);
199
- useEffect(() => {
200
- if (!needsPropertyBridge) return;
201
- if (isClearable === undefined) return;
202
- const el = elementRef.current as any;
203
- if (!el) return;
204
- if (Boolean(el.clearable) !== isClearable) el.clearable = isClearable;
205
- }, [isClearable]);
206
-
207
- // Convert React props to web component attributes
208
- const webComponentProps: Record<string, any> = {
209
- ...props,
210
- ref: elementRef,
211
- };
212
-
213
- if (isDisabled) webComponentProps.disabled = '';
214
- if (isLoading) webComponentProps.loading = '';
215
- if (isExternalSearch) webComponentProps['external-search'] = '';
216
-
217
- // Handle clearable functionality
218
- if (isClearable === true) {
219
- webComponentProps.clearable = '';
220
- } else if (isClearable === false) {
221
- webComponentProps['not-clearable'] = '';
222
- }
223
-
224
- // Add debounce attribute
225
- if (debounce !== undefined) {
226
- webComponentProps.debounce = debounce;
227
- }
228
-
229
- // Add string attributes
230
- if (name) webComponentProps.name = name;
231
-
232
- return React.createElement(
233
- 'ty-dropdown',
234
- webComponentProps,
235
- renderContent()
236
- );
237
- }
238
- );
239
-
240
- TyDropdown.displayName = 'TyDropdown';
@@ -1,208 +0,0 @@
1
- import React, { useEffect, useRef, useCallback } from 'react';
2
- import { needsPropertyBridge } from '../utils/react-version';
3
- import { useBooleanProperty } from '../utils/use-boolean-prop';
4
-
5
- // Type definitions for Ty Multiselect component
6
- export interface TyMultiselectEventDetail {
7
- /** Array of currently selected values */
8
- values: string[];
9
- /** Action that triggered the change: "add" | "remove" */
10
- action: 'add' | 'remove';
11
- /** The specific item that was added or removed */
12
- item: string;
13
- }
14
-
15
- export interface TyMultiselectProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'style'> {
16
- style?: import('./TyInput').TyInputCSSProperties;
17
- /** Current selected values as comma-separated string or array */
18
- value?: string | string[];
19
-
20
- /** Placeholder text when no items are selected */
21
- placeholder?: string;
22
-
23
- /** Disable the multiselect component */
24
- disabled?: boolean;
25
-
26
- /**
27
- * Loading state — replaces the available-options area with a centered
28
- * spinner. Search input stays usable. Pair with `externalSearch` while
29
- * fetching results from a parent-owned data source.
30
- */
31
- loading?: boolean;
32
-
33
- /** Make the multiselect read-only */
34
- readonly?: boolean;
35
-
36
- /** Semantic styling variant */
37
- flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
38
-
39
- /** Label text for the multiselect */
40
- label?: string;
41
-
42
- /** Mark the field as required */
43
- required?: boolean;
44
-
45
- /**
46
- * Switch to external (remote) search mode. Default is `false` — the
47
- * multiselect filters its own children client-side. Set to `true` when you
48
- * want to handle filtering yourself (e.g. server-side): the component will
49
- * dispatch `search` events on each keystroke, and you must update the children
50
- * in response. The `onSearch` prop receives those events.
51
- */
52
- externalSearch?: boolean;
53
-
54
- /** Debounce in milliseconds (0-5000) */
55
- debounce?: number;
56
-
57
- /** Mobile section label for selected items */
58
- selectedLabel?: string;
59
-
60
- /** Form field name for form submission */
61
- name?: string;
62
-
63
- /** Callback when selection changes */
64
- onChange?: (event: CustomEvent<TyMultiselectEventDetail>) => void;
65
-
66
- /** Callback fired on each search input change (debounced by `debounce`). Use for external/server-side filtering. */
67
- onSearch?: (event: CustomEvent<{ query: string; element: HTMLElement }>) => void;
68
-
69
- /** Children should be TyTag components, not TyOption */
70
- children?: React.ReactNode;
71
- }
72
-
73
- // React wrapper for ty-multiselect web component
74
- export const TyMultiselect = React.forwardRef<HTMLElement, TyMultiselectProps>(
75
- ({
76
- value,
77
- placeholder,
78
- disabled,
79
- loading,
80
- readonly,
81
- flavor,
82
- label,
83
- required,
84
- externalSearch,
85
- debounce,
86
- selectedLabel,
87
- name,
88
- onChange,
89
- onSearch,
90
- children,
91
- ...props
92
- }, ref) => {
93
- const elementRef = useRef<HTMLElement>(null);
94
-
95
- // Handle ref forwarding
96
- useEffect(() => {
97
- if (ref && elementRef.current) {
98
- if (typeof ref === 'function') {
99
- ref(elementRef.current);
100
- } else {
101
- ref.current = elementRef.current;
102
- }
103
- }
104
- }, [ref]);
105
-
106
- // Imperatively sync `value` to the underlying property so resets
107
- // (`value=""` or null) reliably clear the visible selection.
108
- // React 18 workaround; React 19+ handles this natively.
109
- useEffect(() => {
110
- if (!needsPropertyBridge) return;
111
- const element = elementRef.current as any;
112
- if (!element) return;
113
- const next = (props as any).value ?? '';
114
- if (element.value !== next) {
115
- element.value = next;
116
- }
117
- }, [(props as any).value]);
118
-
119
- // Handle change events
120
- const handleChange = useCallback((event: Event) => {
121
- const customEvent = event as CustomEvent<TyMultiselectEventDetail>;
122
- if (onChange) {
123
- onChange(customEvent);
124
- }
125
- }, [onChange]);
126
-
127
- const handleSearch = useCallback((event: Event) => {
128
- const customEvent = event as CustomEvent<{ query: string; element: HTMLElement }>;
129
- if (onSearch) {
130
- onSearch(customEvent);
131
- }
132
- }, [onSearch]);
133
-
134
- // Set up event listeners
135
- useEffect(() => {
136
- const element = elementRef.current;
137
- if (!element) return;
138
-
139
- if (onChange) element.addEventListener('change', handleChange);
140
- if (onSearch) element.addEventListener('search', handleSearch);
141
-
142
- return () => {
143
- if (onChange) element.removeEventListener('change', handleChange);
144
- if (onSearch) element.removeEventListener('search', handleSearch);
145
- };
146
- }, [handleChange, handleSearch, onChange, onSearch]);
147
-
148
- // Imperative property sync for boolean props (see use-boolean-prop.ts).
149
- const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
150
- const isLoading = useBooleanProperty(elementRef, 'loading', loading);
151
- const isReadonly = useBooleanProperty(elementRef, 'readonly', readonly);
152
- const isRequired = useBooleanProperty(elementRef, 'required', required);
153
- const isExternalSearch = useBooleanProperty(elementRef, 'externalSearch', externalSearch);
154
-
155
- // Convert React props to web component attributes
156
- const webComponentProps: Record<string, any> = {
157
- ...props,
158
- ref: elementRef,
159
- };
160
-
161
- // Handle value conversion (array to comma-separated string)
162
- if (value !== undefined) {
163
- const valueString = Array.isArray(value) ? value.join(',') : value;
164
- webComponentProps.value = valueString;
165
- }
166
-
167
- // Add optional attributes only if they have values
168
- if (placeholder) {
169
- webComponentProps.placeholder = placeholder;
170
- }
171
-
172
- if (isLoading) webComponentProps.loading = '';
173
- if (isDisabled) webComponentProps.disabled = '';
174
- if (isReadonly) webComponentProps.readonly = '';
175
- if (isRequired) webComponentProps.required = '';
176
- if (isExternalSearch) webComponentProps['external-search'] = '';
177
-
178
- if (flavor) {
179
- webComponentProps.flavor = flavor;
180
- }
181
-
182
- if (label) {
183
- webComponentProps.label = label;
184
- }
185
-
186
- if (name) {
187
- webComponentProps.name = name;
188
- }
189
-
190
- // Add debounce attribute
191
- if (debounce !== undefined) {
192
- webComponentProps.debounce = debounce;
193
- }
194
-
195
- // Add selectedLabel attribute
196
- if (selectedLabel) {
197
- webComponentProps['selected-label'] = selectedLabel;
198
- }
199
-
200
- return React.createElement(
201
- 'ty-multiselect',
202
- webComponentProps,
203
- children
204
- );
205
- }
206
- );
207
-
208
- TyMultiselect.displayName = 'TyMultiselect';