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.
- package/dist/components/TyCalendar.d.ts +6 -0
- package/dist/components/TyCalendar.d.ts.map +1 -1
- package/dist/components/TyCalendar.js +16 -2
- package/dist/components/TyCalendar.js.map +1 -1
- package/dist/components/TyCalendarMonth.d.ts +4 -0
- package/dist/components/TyCalendarMonth.d.ts.map +1 -1
- package/dist/components/TyCalendarMonth.js +5 -1
- package/dist/components/TyCalendarMonth.js.map +1 -1
- package/dist/components/TyCalendarNavigation.d.ts +4 -0
- package/dist/components/TyCalendarNavigation.d.ts.map +1 -1
- package/dist/components/TyCalendarNavigation.js +5 -1
- package/dist/components/TyCalendarNavigation.js.map +1 -1
- package/dist/components/TyCheckbox.d.ts +2 -0
- package/dist/components/TyCheckbox.d.ts.map +1 -1
- package/dist/components/TyCheckbox.js +4 -1
- package/dist/components/TyCheckbox.js.map +1 -1
- package/dist/components/TyCopy.d.ts +4 -0
- package/dist/components/TyCopy.d.ts.map +1 -1
- package/dist/components/TyCopy.js +19 -1
- package/dist/components/TyCopy.js.map +1 -1
- package/dist/components/TyDatePicker.d.ts +4 -0
- package/dist/components/TyDatePicker.d.ts.map +1 -1
- package/dist/components/TyDatePicker.js +7 -1
- package/dist/components/TyDatePicker.js.map +1 -1
- package/dist/components/TyOption.d.ts +8 -0
- package/dist/components/TyOption.d.ts.map +1 -1
- package/dist/components/TyOption.js +3 -1
- package/dist/components/TyOption.js.map +1 -1
- package/dist/components/TyScrollContainer.d.ts +26 -1
- package/dist/components/TyScrollContainer.d.ts.map +1 -1
- package/dist/components/TyScrollContainer.js +41 -24
- package/dist/components/TyScrollContainer.js.map +1 -1
- package/dist/components/TySelect.d.ts +75 -0
- package/dist/components/TySelect.d.ts.map +1 -0
- package/dist/components/TySelect.js +134 -0
- package/dist/components/TySelect.js.map +1 -0
- package/dist/components/index.d.ts +6 -8
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -4
- package/dist/components/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/components/TyCalendar.tsx +30 -1
- package/src/components/TyCalendarMonth.tsx +11 -1
- package/src/components/TyCalendarNavigation.tsx +11 -1
- package/src/components/TyCheckbox.tsx +8 -2
- package/src/components/TyCopy.tsx +25 -1
- package/src/components/TyDatePicker.tsx +16 -0
- package/src/components/TyOption.tsx +14 -1
- package/src/components/TyScrollContainer.tsx +74 -24
- package/src/components/TySelect.tsx +240 -0
- package/src/components/index.ts +8 -13
- package/src/version.ts +1 -1
- package/dist/components/TyDropdown.d.ts +0 -62
- package/dist/components/TyDropdown.d.ts.map +0 -1
- package/dist/components/TyDropdown.js +0 -124
- package/dist/components/TyDropdown.js.map +0 -1
- package/dist/components/TyMultiselect.d.ts +0 -57
- package/dist/components/TyMultiselect.d.ts.map +0 -1
- package/dist/components/TyMultiselect.js +0 -111
- package/dist/components/TyMultiselect.js.map +0 -1
- package/src/components/EventConventionTest.tsx +0 -155
- package/src/components/TyDropdown.tsx +0 -240
- package/src/components/TyMultiselect.tsx +0 -208
|
@@ -26,6 +26,12 @@ export interface TyCopyProps extends Omit<React.HTMLAttributes<HTMLElement>, 'on
|
|
|
26
26
|
|
|
27
27
|
/** Required field */
|
|
28
28
|
required?: boolean;
|
|
29
|
+
|
|
30
|
+
/** Fired after the value is copied to the clipboard */
|
|
31
|
+
onCopySuccess?: (event: CustomEvent) => void;
|
|
32
|
+
|
|
33
|
+
/** Fired when copying to the clipboard fails */
|
|
34
|
+
onCopyError?: (event: CustomEvent) => void;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
// React wrapper for ty-copy web component
|
|
@@ -39,7 +45,9 @@ export const TyCopy = React.forwardRef<HTMLElement, TyCopyProps>(
|
|
|
39
45
|
multiline,
|
|
40
46
|
disabled,
|
|
41
47
|
required,
|
|
42
|
-
|
|
48
|
+
onCopySuccess,
|
|
49
|
+
onCopyError,
|
|
50
|
+
...props
|
|
43
51
|
}, ref) => {
|
|
44
52
|
const elementRef = useRef<HTMLElement>(null);
|
|
45
53
|
|
|
@@ -54,6 +62,22 @@ export const TyCopy = React.forwardRef<HTMLElement, TyCopyProps>(
|
|
|
54
62
|
}
|
|
55
63
|
}, [ref]);
|
|
56
64
|
|
|
65
|
+
// Custom events → React callbacks
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const el = elementRef.current;
|
|
68
|
+
if (!el) return;
|
|
69
|
+
const bound: Array<[string, EventListener]> = [];
|
|
70
|
+
if (onCopySuccess) {
|
|
71
|
+
const h = (e: Event) => onCopySuccess(e as CustomEvent);
|
|
72
|
+
el.addEventListener('copy-success', h); bound.push(['copy-success', h]);
|
|
73
|
+
}
|
|
74
|
+
if (onCopyError) {
|
|
75
|
+
const h = (e: Event) => onCopyError(e as CustomEvent);
|
|
76
|
+
el.addEventListener('copy-error', h); bound.push(['copy-error', h]);
|
|
77
|
+
}
|
|
78
|
+
return () => bound.forEach(([n, h]) => el.removeEventListener(n, h));
|
|
79
|
+
}, [onCopySuccess, onCopyError]);
|
|
80
|
+
|
|
57
81
|
const isMultiline = useBooleanProperty(elementRef, 'multiline', multiline);
|
|
58
82
|
const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
|
|
59
83
|
const isRequired = useBooleanProperty(elementRef, 'required', required);
|
|
@@ -50,6 +50,12 @@ export interface TyDatePickerProps extends Omit<React.HTMLAttributes<HTMLElement
|
|
|
50
50
|
|
|
51
51
|
/** Whether to include time selection */
|
|
52
52
|
withTime?: boolean;
|
|
53
|
+
|
|
54
|
+
/** Earliest selectable date (ISO "YYYY-MM-DD") */
|
|
55
|
+
min?: string;
|
|
56
|
+
|
|
57
|
+
/** Latest selectable date (ISO "YYYY-MM-DD") */
|
|
58
|
+
max?: string;
|
|
53
59
|
|
|
54
60
|
/** Callback when the date value changes */
|
|
55
61
|
onChange?: (event: CustomEvent<TyDatePickerEventDetail>) => void;
|
|
@@ -76,6 +82,8 @@ export const TyDatePicker = React.forwardRef<HTMLElement, TyDatePickerProps>(
|
|
|
76
82
|
format,
|
|
77
83
|
locale,
|
|
78
84
|
withTime,
|
|
85
|
+
min,
|
|
86
|
+
max,
|
|
79
87
|
onChange,
|
|
80
88
|
onOpen,
|
|
81
89
|
onClose,
|
|
@@ -208,6 +216,14 @@ export const TyDatePicker = React.forwardRef<HTMLElement, TyDatePickerProps>(
|
|
|
208
216
|
webComponentProps['with-time'] = ''; // Convert camelCase to kebab-case
|
|
209
217
|
}
|
|
210
218
|
|
|
219
|
+
if (min) {
|
|
220
|
+
webComponentProps.min = min;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (max) {
|
|
224
|
+
webComponentProps.max = max;
|
|
225
|
+
}
|
|
226
|
+
|
|
211
227
|
return React.createElement('ty-date-picker', webComponentProps);
|
|
212
228
|
}
|
|
213
229
|
);
|
|
@@ -4,6 +4,17 @@ import { useBooleanProperty } from '../utils/use-boolean-prop';
|
|
|
4
4
|
// Type definitions for Ty Option component
|
|
5
5
|
export interface TyOptionProps extends React.HTMLAttributes<HTMLElement> {
|
|
6
6
|
value?: string;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Clean display text (native <option label> semantics) — used by ty-select
|
|
10
|
+
* for field summaries and by ty-selected-tags chips when the option's
|
|
11
|
+
* children are rich HTML. data-* attributes feed chip templates.
|
|
12
|
+
*/
|
|
13
|
+
label?: string;
|
|
14
|
+
|
|
15
|
+
/** Semantic flavor — carried onto ty-selected-tags chips */
|
|
16
|
+
flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral' | 'info';
|
|
17
|
+
|
|
7
18
|
disabled?: boolean;
|
|
8
19
|
selected?: boolean;
|
|
9
20
|
hidden?: boolean;
|
|
@@ -12,7 +23,7 @@ export interface TyOptionProps extends React.HTMLAttributes<HTMLElement> {
|
|
|
12
23
|
|
|
13
24
|
// React wrapper for ty-option web component
|
|
14
25
|
export const TyOption = React.forwardRef<HTMLElement, TyOptionProps>(
|
|
15
|
-
({ children, disabled, selected, hidden, ...props }, ref) => {
|
|
26
|
+
({ children, label, flavor, disabled, selected, hidden, ...props }, ref) => {
|
|
16
27
|
const elementRef = useRef<HTMLElement>(null);
|
|
17
28
|
|
|
18
29
|
// Handle ref forwarding
|
|
@@ -34,6 +45,8 @@ export const TyOption = React.forwardRef<HTMLElement, TyOptionProps>(
|
|
|
34
45
|
'ty-option',
|
|
35
46
|
{
|
|
36
47
|
...props,
|
|
48
|
+
...(label && { label }),
|
|
49
|
+
...(flavor && { flavor }),
|
|
37
50
|
...(isDisabled && { disabled: "" }),
|
|
38
51
|
...(isSelected && { selected: "" }),
|
|
39
52
|
...(isHidden && { hidden: "" }),
|
|
@@ -2,6 +2,14 @@ import React, { useEffect, useRef, useImperativeHandle } from 'react';
|
|
|
2
2
|
import { needsPropertyBridge } from '../utils/react-version';
|
|
3
3
|
import { useBooleanProperty, coerceBool } from '../utils/use-boolean-prop';
|
|
4
4
|
|
|
5
|
+
/** Detail payload for nearstart / nearend events. */
|
|
6
|
+
export interface TyScrollNearEdgeDetail {
|
|
7
|
+
distance: number;
|
|
8
|
+
scrollTop: number;
|
|
9
|
+
scrollHeight: number;
|
|
10
|
+
clientHeight: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
// Type definitions for Ty ScrollContainer component
|
|
6
14
|
export interface TyScrollContainerProps extends React.HTMLAttributes<HTMLElement> {
|
|
7
15
|
/** Maximum height of the scroll container */
|
|
@@ -10,9 +18,27 @@ export interface TyScrollContainerProps extends React.HTMLAttributes<HTMLElement
|
|
|
10
18
|
/** Enable/disable scroll shadows (default: true) */
|
|
11
19
|
shadow?: boolean;
|
|
12
20
|
|
|
13
|
-
/** Hide native scrollbar */
|
|
21
|
+
/** Hide native scrollbar (no custom scrollbar) */
|
|
14
22
|
hideScrollbar?: boolean;
|
|
15
23
|
|
|
24
|
+
/** Use the styled custom scrollbar */
|
|
25
|
+
customScrollbar?: boolean;
|
|
26
|
+
|
|
27
|
+
/** Enable horizontal scrolling */
|
|
28
|
+
overflowX?: boolean;
|
|
29
|
+
|
|
30
|
+
/** Preserve visual position when content is prepended above the viewport */
|
|
31
|
+
scrollAnchoring?: boolean;
|
|
32
|
+
|
|
33
|
+
/** Distance (px) from an edge at which nearstart/nearend fire (default 100) */
|
|
34
|
+
nearEdgeThreshold?: number;
|
|
35
|
+
|
|
36
|
+
/** Fired once when scrolled within the threshold of the bottom */
|
|
37
|
+
onNearEnd?: (event: CustomEvent<TyScrollNearEdgeDetail>) => void;
|
|
38
|
+
|
|
39
|
+
/** Fired once when scrolled within the threshold of the top */
|
|
40
|
+
onNearStart?: (event: CustomEvent<TyScrollNearEdgeDetail>) => void;
|
|
41
|
+
|
|
16
42
|
/** Content to scroll */
|
|
17
43
|
children?: React.ReactNode;
|
|
18
44
|
}
|
|
@@ -25,6 +51,12 @@ export interface TyScrollContainerRef {
|
|
|
25
51
|
scrollToTop: (smooth?: boolean) => void;
|
|
26
52
|
/** Scroll to bottom */
|
|
27
53
|
scrollToBottom: (smooth?: boolean) => void;
|
|
54
|
+
/** Scroll to far left */
|
|
55
|
+
scrollToLeft: (smooth?: boolean) => void;
|
|
56
|
+
/** Scroll to far right */
|
|
57
|
+
scrollToRight: (smooth?: boolean) => void;
|
|
58
|
+
/** Scroll a descendant (element or CSS selector) into view */
|
|
59
|
+
scrollToElement: (target: Element | string, smooth?: boolean) => void;
|
|
28
60
|
/** Get the underlying scroll element */
|
|
29
61
|
scrollElement: HTMLElement | null;
|
|
30
62
|
/** Get the native element */
|
|
@@ -38,35 +70,29 @@ export const TyScrollContainer = React.forwardRef<TyScrollContainerRef, TyScroll
|
|
|
38
70
|
maxHeight,
|
|
39
71
|
shadow,
|
|
40
72
|
hideScrollbar,
|
|
73
|
+
customScrollbar,
|
|
74
|
+
overflowX,
|
|
75
|
+
scrollAnchoring,
|
|
76
|
+
nearEdgeThreshold,
|
|
77
|
+
onNearEnd,
|
|
78
|
+
onNearStart,
|
|
41
79
|
...props
|
|
42
80
|
}, ref) => {
|
|
43
81
|
const elementRef = useRef<HTMLElement>(null);
|
|
44
82
|
|
|
45
83
|
// Expose imperative methods via ref
|
|
46
84
|
useImperativeHandle(ref, () => ({
|
|
47
|
-
updateShadows: () => {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
}
|
|
85
|
+
updateShadows: () => { (elementRef.current as any)?.updateShadows?.(); },
|
|
86
|
+
scrollToTop: (smooth = true) => { (elementRef.current as any)?.scrollToTop?.(smooth); },
|
|
87
|
+
scrollToBottom: (smooth = true) => { (elementRef.current as any)?.scrollToBottom?.(smooth); },
|
|
88
|
+
scrollToLeft: (smooth = true) => { (elementRef.current as any)?.scrollToLeft?.(smooth); },
|
|
89
|
+
scrollToRight: (smooth = true) => { (elementRef.current as any)?.scrollToRight?.(smooth); },
|
|
90
|
+
scrollToElement: (target, smooth = true) => { (elementRef.current as any)?.scrollToElement?.(target, smooth); },
|
|
91
|
+
get scrollElement() { return (elementRef.current as any)?.scrollElement ?? null; },
|
|
92
|
+
get element() { return elementRef.current; }
|
|
66
93
|
}), []);
|
|
67
94
|
|
|
68
|
-
// shadow defaults to true;
|
|
69
|
-
// attribute level. Bridge it imperatively so flipping back to true
|
|
95
|
+
// shadow defaults to true; bridge it imperatively so flipping back to true
|
|
70
96
|
// propagates on React 18.
|
|
71
97
|
useEffect(() => {
|
|
72
98
|
if (!needsPropertyBridge) return;
|
|
@@ -76,7 +102,27 @@ export const TyScrollContainer = React.forwardRef<TyScrollContainerRef, TyScroll
|
|
|
76
102
|
const next = coerceBool(shadow);
|
|
77
103
|
if (Boolean(el.shadow) !== next) el.shadow = next;
|
|
78
104
|
}, [shadow]);
|
|
105
|
+
|
|
79
106
|
const isHideScrollbar = useBooleanProperty(elementRef, 'hideScrollbar', hideScrollbar);
|
|
107
|
+
const isCustomScrollbar = useBooleanProperty(elementRef, 'customScrollbar', customScrollbar);
|
|
108
|
+
const isOverflowX = useBooleanProperty(elementRef, 'overflowX', overflowX);
|
|
109
|
+
const isScrollAnchoring = useBooleanProperty(elementRef, 'scrollAnchoring', scrollAnchoring);
|
|
110
|
+
|
|
111
|
+
// Custom events → React callbacks
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
const el = elementRef.current;
|
|
114
|
+
if (!el) return;
|
|
115
|
+
const bound: Array<[string, EventListener]> = [];
|
|
116
|
+
if (onNearEnd) {
|
|
117
|
+
const h = (e: Event) => onNearEnd(e as CustomEvent<TyScrollNearEdgeDetail>);
|
|
118
|
+
el.addEventListener('nearend', h); bound.push(['nearend', h]);
|
|
119
|
+
}
|
|
120
|
+
if (onNearStart) {
|
|
121
|
+
const h = (e: Event) => onNearStart(e as CustomEvent<TyScrollNearEdgeDetail>);
|
|
122
|
+
el.addEventListener('nearstart', h); bound.push(['nearstart', h]);
|
|
123
|
+
}
|
|
124
|
+
return () => bound.forEach(([n, h]) => el.removeEventListener(n, h));
|
|
125
|
+
}, [onNearEnd, onNearStart]);
|
|
80
126
|
|
|
81
127
|
// Convert React props to web component attributes
|
|
82
128
|
const webComponentProps: Record<string, any> = {
|
|
@@ -84,12 +130,16 @@ export const TyScrollContainer = React.forwardRef<TyScrollContainerRef, TyScroll
|
|
|
84
130
|
ref: elementRef,
|
|
85
131
|
};
|
|
86
132
|
|
|
87
|
-
//
|
|
133
|
+
// String attributes
|
|
88
134
|
if (maxHeight) webComponentProps['max-height'] = maxHeight;
|
|
135
|
+
if (nearEdgeThreshold != null) webComponentProps['near-edge-threshold'] = String(nearEdgeThreshold);
|
|
89
136
|
|
|
90
|
-
//
|
|
137
|
+
// Boolean attributes
|
|
91
138
|
if (shadow !== undefined && !coerceBool(shadow)) webComponentProps.shadow = 'false';
|
|
92
139
|
if (isHideScrollbar) webComponentProps['hide-scrollbar'] = '';
|
|
140
|
+
if (isCustomScrollbar) webComponentProps['custom-scrollbar'] = '';
|
|
141
|
+
if (isOverflowX) webComponentProps['overflow-x'] = '';
|
|
142
|
+
if (isScrollAnchoring) webComponentProps['scroll-anchoring'] = '';
|
|
93
143
|
|
|
94
144
|
return React.createElement(
|
|
95
145
|
'ty-scroll-container',
|
|
@@ -0,0 +1,240 @@
|
|
|
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 Select component
|
|
6
|
+
export interface TySelectItem {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
flavor: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TySelectEventDetail {
|
|
13
|
+
/** Scalar for single select, array when `multiple` */
|
|
14
|
+
value: string | string[] | null;
|
|
15
|
+
/** Always the array form of the selection */
|
|
16
|
+
values: string[];
|
|
17
|
+
/** Rich info per selected value — enough to render chips out-of-band */
|
|
18
|
+
items: TySelectItem[];
|
|
19
|
+
/** Action that triggered the change */
|
|
20
|
+
action: 'add' | 'remove' | 'clear' | 'set';
|
|
21
|
+
/** The specific value that changed */
|
|
22
|
+
item: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TySelectProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'style'> {
|
|
26
|
+
style?: import('./TyInput').TyInputCSSProperties;
|
|
27
|
+
|
|
28
|
+
/** Selected value(s) — string, comma-separated string, or array */
|
|
29
|
+
value?: string | string[];
|
|
30
|
+
|
|
31
|
+
/** Multi select (native <select multiple> semantics). Default: single. */
|
|
32
|
+
multiple?: boolean;
|
|
33
|
+
|
|
34
|
+
/** Compact content-hugging trigger (toolbars, filter bars) instead of the full-width field look */
|
|
35
|
+
compact?: boolean;
|
|
36
|
+
|
|
37
|
+
/** Placeholder shown while nothing is selected */
|
|
38
|
+
placeholder?: string;
|
|
39
|
+
|
|
40
|
+
/** Label text above the field */
|
|
41
|
+
label?: string;
|
|
42
|
+
|
|
43
|
+
/** Form field name — single submits one entry, multiple submits repeated entries */
|
|
44
|
+
name?: string;
|
|
45
|
+
|
|
46
|
+
/** Disable the select */
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
|
|
49
|
+
/** Read-only */
|
|
50
|
+
readonly?: boolean;
|
|
51
|
+
|
|
52
|
+
/** Mark the field as required */
|
|
53
|
+
required?: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Search row visibility: 'auto' (default) shows it only for 8+ options,
|
|
57
|
+
* 'always' / 'never' force it. external-search always shows it.
|
|
58
|
+
*/
|
|
59
|
+
searchable?: 'auto' | 'always' | 'never' | boolean;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* External (remote) search mode — the component stops filtering and emits
|
|
63
|
+
* debounced `search` events; replace the option children in response.
|
|
64
|
+
*/
|
|
65
|
+
externalSearch?: boolean;
|
|
66
|
+
|
|
67
|
+
/** Debounce for the search event in ms (0-5000) */
|
|
68
|
+
debounce?: number;
|
|
69
|
+
|
|
70
|
+
/** Loading state — shows a spinner in the options area (external search in flight) */
|
|
71
|
+
loading?: boolean;
|
|
72
|
+
|
|
73
|
+
/** Size variant */
|
|
74
|
+
size?: 'sm' | 'md' | 'lg';
|
|
75
|
+
|
|
76
|
+
/** Callback when selection changes */
|
|
77
|
+
onChange?: (event: CustomEvent<TySelectEventDetail>) => void;
|
|
78
|
+
|
|
79
|
+
/** Callback fired on each search input change (debounced). Use for external/server-side filtering. */
|
|
80
|
+
onSearch?: (event: CustomEvent<{ query: string; element: HTMLElement }>) => void;
|
|
81
|
+
onOpen?: (event: CustomEvent) => void;
|
|
82
|
+
onClose?: (event: CustomEvent) => void;
|
|
83
|
+
|
|
84
|
+
/** TyOption children (plus optional slot="trigger" / slot="start" / slot="end" elements) */
|
|
85
|
+
children?: React.ReactNode;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// React wrapper for ty-select web component
|
|
89
|
+
export const TySelect = React.forwardRef<HTMLElement, TySelectProps>(
|
|
90
|
+
({
|
|
91
|
+
value,
|
|
92
|
+
multiple,
|
|
93
|
+
compact,
|
|
94
|
+
placeholder,
|
|
95
|
+
label,
|
|
96
|
+
name,
|
|
97
|
+
disabled,
|
|
98
|
+
readonly,
|
|
99
|
+
required,
|
|
100
|
+
searchable,
|
|
101
|
+
externalSearch,
|
|
102
|
+
debounce,
|
|
103
|
+
loading,
|
|
104
|
+
size,
|
|
105
|
+
onChange,
|
|
106
|
+
onSearch,
|
|
107
|
+
onOpen,
|
|
108
|
+
onClose,
|
|
109
|
+
children,
|
|
110
|
+
...props
|
|
111
|
+
}, ref) => {
|
|
112
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
113
|
+
|
|
114
|
+
// Handle ref forwarding
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (ref && elementRef.current) {
|
|
117
|
+
if (typeof ref === 'function') {
|
|
118
|
+
ref(elementRef.current);
|
|
119
|
+
} else {
|
|
120
|
+
ref.current = elementRef.current;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, [ref]);
|
|
124
|
+
|
|
125
|
+
// Imperatively sync `value` so resets ('' or null) reliably clear the
|
|
126
|
+
// visible selection. React 18 workaround; React 19+ handles this natively.
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!needsPropertyBridge) return;
|
|
129
|
+
const element = elementRef.current as any;
|
|
130
|
+
if (!element) return;
|
|
131
|
+
const next = Array.isArray(value) ? value.join(',') : (value ?? '');
|
|
132
|
+
if (element.value !== next) {
|
|
133
|
+
element.value = next;
|
|
134
|
+
}
|
|
135
|
+
}, [value]);
|
|
136
|
+
|
|
137
|
+
// Handle events
|
|
138
|
+
const handleChange = useCallback((event: Event) => {
|
|
139
|
+
if (onChange) onChange(event as CustomEvent<TySelectEventDetail>);
|
|
140
|
+
}, [onChange]);
|
|
141
|
+
|
|
142
|
+
const handleSearch = useCallback((event: Event) => {
|
|
143
|
+
if (onSearch) onSearch(event as CustomEvent<{ query: string; element: HTMLElement }>);
|
|
144
|
+
}, [onSearch]);
|
|
145
|
+
|
|
146
|
+
const handleOpen = useCallback((event: Event) => { if (onOpen) onOpen(event as CustomEvent); }, [onOpen]);
|
|
147
|
+
const handleClose = useCallback((event: Event) => { if (onClose) onClose(event as CustomEvent); }, [onClose]);
|
|
148
|
+
|
|
149
|
+
// Set up event listeners
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
const element = elementRef.current;
|
|
152
|
+
if (!element) return;
|
|
153
|
+
|
|
154
|
+
if (onChange) element.addEventListener('change', handleChange);
|
|
155
|
+
if (onSearch) element.addEventListener('search', handleSearch);
|
|
156
|
+
if (onOpen) element.addEventListener('open', handleOpen);
|
|
157
|
+
if (onClose) element.addEventListener('close', handleClose);
|
|
158
|
+
|
|
159
|
+
return () => {
|
|
160
|
+
if (onChange) element.removeEventListener('change', handleChange);
|
|
161
|
+
if (onSearch) element.removeEventListener('search', handleSearch);
|
|
162
|
+
if (onOpen) element.removeEventListener('open', handleOpen);
|
|
163
|
+
if (onClose) element.removeEventListener('close', handleClose);
|
|
164
|
+
};
|
|
165
|
+
}, [handleChange, handleSearch, handleOpen, handleClose, onChange, onSearch, onOpen, onClose]);
|
|
166
|
+
|
|
167
|
+
// Imperative property sync for boolean props (see use-boolean-prop.ts).
|
|
168
|
+
const isMultiple = useBooleanProperty(elementRef, 'multiple', multiple);
|
|
169
|
+
const isCompact = useBooleanProperty(elementRef, 'compact', compact);
|
|
170
|
+
const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
|
|
171
|
+
const isReadonly = useBooleanProperty(elementRef, 'readonly', readonly);
|
|
172
|
+
const isRequired = useBooleanProperty(elementRef, 'required', required);
|
|
173
|
+
const isLoading = useBooleanProperty(elementRef, 'loading', loading);
|
|
174
|
+
const isExternalSearch = useBooleanProperty(elementRef, 'externalSearch', externalSearch);
|
|
175
|
+
|
|
176
|
+
// Convert React props to web component attributes
|
|
177
|
+
const webComponentProps: Record<string, any> = {
|
|
178
|
+
...props,
|
|
179
|
+
ref: elementRef,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Handle value conversion (array to comma-separated string)
|
|
183
|
+
if (value !== undefined) {
|
|
184
|
+
webComponentProps.value = Array.isArray(value) ? value.join(',') : value;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (isMultiple) webComponentProps.multiple = '';
|
|
188
|
+
if (isCompact) webComponentProps.compact = '';
|
|
189
|
+
if (isDisabled) webComponentProps.disabled = '';
|
|
190
|
+
if (isReadonly) webComponentProps.readonly = '';
|
|
191
|
+
if (isRequired) webComponentProps.required = '';
|
|
192
|
+
if (isLoading) webComponentProps.loading = '';
|
|
193
|
+
if (isExternalSearch) webComponentProps['external-search'] = '';
|
|
194
|
+
|
|
195
|
+
if (placeholder) webComponentProps.placeholder = placeholder;
|
|
196
|
+
if (label) webComponentProps.label = label;
|
|
197
|
+
if (name) webComponentProps.name = name;
|
|
198
|
+
if (size) webComponentProps.size = size;
|
|
199
|
+
if (debounce !== undefined) webComponentProps.debounce = debounce.toString();
|
|
200
|
+
if (searchable === 'always' || searchable === true) webComponentProps.searchable = 'true';
|
|
201
|
+
else if (searchable === 'never' || searchable === false) webComponentProps.searchable = 'false';
|
|
202
|
+
// 'auto' / undefined → omit (component default)
|
|
203
|
+
|
|
204
|
+
return React.createElement('ty-select', webComponentProps, children);
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
TySelect.displayName = 'TySelect';
|
|
209
|
+
|
|
210
|
+
// ============================================================================
|
|
211
|
+
// TySelectedTags — out-of-band chip display for a TySelect
|
|
212
|
+
// ============================================================================
|
|
213
|
+
|
|
214
|
+
export interface TySelectedTagsProps extends React.HTMLAttributes<HTMLElement> {
|
|
215
|
+
/** id of the ty-select to display. Falls back to the previous element sibling. */
|
|
216
|
+
htmlFor?: string;
|
|
217
|
+
/** Optional <template> child as chip blueprint ({value}/{label}/{flavor}/{data-*} placeholders) */
|
|
218
|
+
children?: React.ReactNode;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// React wrapper for ty-selected-tags web component
|
|
222
|
+
export const TySelectedTags = React.forwardRef<HTMLElement, TySelectedTagsProps>(
|
|
223
|
+
({ htmlFor, children, ...props }, ref) => {
|
|
224
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
225
|
+
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (ref && elementRef.current) {
|
|
228
|
+
if (typeof ref === 'function') ref(elementRef.current);
|
|
229
|
+
else ref.current = elementRef.current;
|
|
230
|
+
}
|
|
231
|
+
}, [ref]);
|
|
232
|
+
|
|
233
|
+
const webComponentProps: Record<string, any> = { ...props, ref: elementRef };
|
|
234
|
+
if (htmlFor) webComponentProps.for = htmlFor;
|
|
235
|
+
|
|
236
|
+
return React.createElement('ty-selected-tags', webComponentProps, children);
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
TySelectedTags.displayName = 'TySelectedTags';
|
package/src/components/index.ts
CHANGED
|
@@ -44,9 +44,6 @@ export type { TyInputProps, TyInputEventDetail, TyInputCSSProperties } from './T
|
|
|
44
44
|
export { TyTextarea } from './TyTextarea';
|
|
45
45
|
export type { TyTextareaProps, TyTextareaEventDetail } from './TyTextarea';
|
|
46
46
|
|
|
47
|
-
export { TyDropdown } from './TyDropdown';
|
|
48
|
-
export type { TyDropdownProps, TyDropdownEventDetail } from './TyDropdown';
|
|
49
|
-
|
|
50
47
|
export { TyOption } from './TyOption';
|
|
51
48
|
export type { TyOptionProps } from './TyOption';
|
|
52
49
|
|
|
@@ -55,12 +52,15 @@ export type { TyIconProps } from './TyIcon';
|
|
|
55
52
|
|
|
56
53
|
export { TyModal } from './TyModal';
|
|
57
54
|
export type { TyModalProps, TyModalEventDetail, TyModalRef } from './TyModal';
|
|
55
|
+
// Platform/ARIA name alias (the element is also registered as ty-dialog)
|
|
56
|
+
export { TyModal as TyDialog } from './TyModal';
|
|
57
|
+
export type { TyModalProps as TyDialogProps, TyModalEventDetail as TyDialogEventDetail, TyModalRef as TyDialogRef } from './TyModal';
|
|
58
58
|
|
|
59
59
|
export { TyTooltip } from './TyTooltip';
|
|
60
60
|
export type { TyTooltipProps } from './TyTooltip';
|
|
61
61
|
|
|
62
|
-
export {
|
|
63
|
-
export type {
|
|
62
|
+
export { TySelect, TySelectedTags } from './TySelect';
|
|
63
|
+
export type { TySelectProps, TySelectEventDetail, TySelectItem, TySelectedTagsProps } from './TySelect';
|
|
64
64
|
|
|
65
65
|
export { TyCalendar } from './TyCalendar';
|
|
66
66
|
export type { TyCalendarProps, TyCalendarChangeEventDetail, TyCalendarNavigateEventDetail } from './TyCalendar';
|
|
@@ -85,6 +85,9 @@ export type { TyRadioGroupProps, TyRadioGroupEventDetail } from './TyRadioGroup'
|
|
|
85
85
|
|
|
86
86
|
export { TyCopy } from './TyCopy';
|
|
87
87
|
export type { TyCopyProps } from './TyCopy';
|
|
88
|
+
// Descriptive name alias (the element is also registered as ty-copy-field)
|
|
89
|
+
export { TyCopy as TyCopyField } from './TyCopy';
|
|
90
|
+
export type { TyCopyProps as TyCopyFieldProps } from './TyCopy';
|
|
88
91
|
|
|
89
92
|
export { TyFileUpload } from './TyFileUpload';
|
|
90
93
|
export type { TyFileUploadProps, TyFileUploadEventDetail } from './TyFileUpload';
|
|
@@ -122,12 +125,10 @@ export { TyButton as Button } from './TyButton';
|
|
|
122
125
|
export { TyTag as Tag } from './TyTag';
|
|
123
126
|
export { TyInput as Input } from './TyInput';
|
|
124
127
|
export { TyTextarea as Textarea } from './TyTextarea';
|
|
125
|
-
export { TyDropdown as Dropdown } from './TyDropdown';
|
|
126
128
|
export { TyOption as Option } from './TyOption';
|
|
127
129
|
export { TyIcon as Icon } from './TyIcon';
|
|
128
130
|
export { TyModal as Modal } from './TyModal';
|
|
129
131
|
export { TyTooltip as Tooltip } from './TyTooltip';
|
|
130
|
-
export { TyMultiselect as Multiselect } from './TyMultiselect';
|
|
131
132
|
export { TyCalendar as Calendar } from './TyCalendar';
|
|
132
133
|
export { TyDatePicker as DatePicker } from './TyDatePicker';
|
|
133
134
|
export { TyPopup as Popup } from './TyPopup';
|
|
@@ -162,9 +163,6 @@ export type { TyInputProps as InputProps, TyInputEventDetail as InputEventDetail
|
|
|
162
163
|
// Textarea types
|
|
163
164
|
export type { TyTextareaProps as TextareaProps, TyTextareaEventDetail as TextareaEventDetail } from './TyTextarea';
|
|
164
165
|
|
|
165
|
-
// Dropdown types
|
|
166
|
-
export type { TyDropdownProps as DropdownProps, TyDropdownEventDetail as DropdownEventDetail, OptionData } from './TyDropdown';
|
|
167
|
-
|
|
168
166
|
// Option types
|
|
169
167
|
export type { TyOptionProps as OptionProps } from './TyOption';
|
|
170
168
|
|
|
@@ -177,9 +175,6 @@ export type { TyModalProps as ModalProps, TyModalEventDetail as ModalEventDetail
|
|
|
177
175
|
// Tooltip types
|
|
178
176
|
export type { TyTooltipProps as TooltipProps } from './TyTooltip';
|
|
179
177
|
|
|
180
|
-
// Multiselect types
|
|
181
|
-
export type { TyMultiselectProps as MultiselectProps, TyMultiselectEventDetail as MultiselectEventDetail } from './TyMultiselect';
|
|
182
|
-
|
|
183
178
|
// Calendar types
|
|
184
179
|
export type { TyCalendarProps as CalendarProps, TyCalendarChangeEventDetail as CalendarChangeEventDetail, TyCalendarNavigateEventDetail as CalendarNavigateEventDetail } from './TyCalendar';
|
|
185
180
|
|
package/src/version.ts
CHANGED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export interface OptionData {
|
|
3
|
-
value: string;
|
|
4
|
-
text: string;
|
|
5
|
-
disabled?: boolean;
|
|
6
|
-
}
|
|
7
|
-
export interface TyDropdownEventDetail {
|
|
8
|
-
option: HTMLElement;
|
|
9
|
-
}
|
|
10
|
-
export interface TyDropdownProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'style'> {
|
|
11
|
-
style?: import('./TyInput').TyInputCSSProperties;
|
|
12
|
-
/** Semantic styling variant */
|
|
13
|
-
flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
|
|
14
|
-
/** Dropdown size */
|
|
15
|
-
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
16
|
-
/** Selected value */
|
|
17
|
-
value?: string;
|
|
18
|
-
/** Placeholder text */
|
|
19
|
-
placeholder?: string;
|
|
20
|
-
/** Dropdown label */
|
|
21
|
-
label?: string;
|
|
22
|
-
/** Disable the dropdown */
|
|
23
|
-
disabled?: boolean;
|
|
24
|
-
/**
|
|
25
|
-
* Loading state — replaces the open popup options list with a centered
|
|
26
|
-
* spinner. Search input stays usable. Pair with `externalSearch` while
|
|
27
|
-
* fetching results from a parent-owned data source.
|
|
28
|
-
*/
|
|
29
|
-
loading?: boolean;
|
|
30
|
-
/** Make dropdown readonly */
|
|
31
|
-
readonly?: boolean;
|
|
32
|
-
/** Required field */
|
|
33
|
-
required?: boolean;
|
|
34
|
-
/** Show clear button */
|
|
35
|
-
clearable?: boolean;
|
|
36
|
-
/** Disable clear button (alias for clearable={false}) */
|
|
37
|
-
notClearable?: boolean;
|
|
38
|
-
/** Debounce in milliseconds (0-5000) */
|
|
39
|
-
debounce?: number;
|
|
40
|
-
/**
|
|
41
|
-
* Switch to external (remote) search mode. Default is `false` — the dropdown
|
|
42
|
-
* filters its options locally. When `true`, the dropdown stops filtering and
|
|
43
|
-
* dispatches `search` events on each keystroke; the parent owns filtering
|
|
44
|
-
* and updates the children.
|
|
45
|
-
*/
|
|
46
|
-
externalSearch?: boolean;
|
|
47
|
-
/** @deprecated Use `externalSearch` instead. */
|
|
48
|
-
notSearchable?: boolean;
|
|
49
|
-
/** @deprecated Use `externalSearch` instead. Pass `searchable={false}` was equivalent to `externalSearch={true}`. */
|
|
50
|
-
searchable?: boolean;
|
|
51
|
-
/** Form field name for form submission */
|
|
52
|
-
name?: string;
|
|
53
|
-
options?: OptionData[];
|
|
54
|
-
onChange?: (event: CustomEvent<TyDropdownEventDetail>) => void;
|
|
55
|
-
onSearch?: (event: CustomEvent<{
|
|
56
|
-
query: string;
|
|
57
|
-
element: HTMLElement;
|
|
58
|
-
}>) => void;
|
|
59
|
-
children?: React.ReactNode;
|
|
60
|
-
}
|
|
61
|
-
export declare const TyDropdown: React.ForwardRefExoticComponent<TyDropdownProps & React.RefAttributes<HTMLElement>>;
|
|
62
|
-
//# sourceMappingURL=TyDropdown.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TyDropdown.d.ts","sourceRoot":"","sources":["../../src/components/TyDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAK9D,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,WAAW,CAAC;CACrB;AAGD,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IACpG,KAAK,CAAC,EAAE,OAAO,WAAW,EAAE,oBAAoB,CAAC;IACjD,+BAA+B;IAC/B,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAEhF,oBAAoB;IACpB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAExC,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,wBAAwB;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,yDAAyD;IACzD,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,gDAAgD;IAChD,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,qHAAqH;IACrH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IAGvB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,qBAAqB,CAAC,KAAK,IAAI,CAAC;IAC/D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IAGjF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAGD,eAAO,MAAM,UAAU,qFAqJtB,CAAC"}
|