tyrell-react 1.0.0-RC11 → 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
|
@@ -1,124 +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
|
-
// React wrapper for ty-dropdown web component
|
|
5
|
-
export const TyDropdown = React.forwardRef(({ options, children, onChange, onSearch, disabled, loading, externalSearch, notSearchable, searchable, clearable, notClearable, debounce, name, value, ...props }, ref) => {
|
|
6
|
-
const elementRef = useRef(null);
|
|
7
|
-
const handleChange = useCallback((event) => {
|
|
8
|
-
if (onChange) {
|
|
9
|
-
onChange(event);
|
|
10
|
-
}
|
|
11
|
-
}, [onChange]);
|
|
12
|
-
const handleSearch = useCallback((event) => {
|
|
13
|
-
if (onSearch) {
|
|
14
|
-
onSearch(event);
|
|
15
|
-
}
|
|
16
|
-
}, [onSearch]);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
const element = elementRef.current;
|
|
19
|
-
if (!element)
|
|
20
|
-
return;
|
|
21
|
-
// Listen for custom events from ty-dropdown
|
|
22
|
-
if (onChange) {
|
|
23
|
-
element.addEventListener('change', handleChange);
|
|
24
|
-
}
|
|
25
|
-
if (onSearch) {
|
|
26
|
-
element.addEventListener('search', handleSearch);
|
|
27
|
-
}
|
|
28
|
-
return () => {
|
|
29
|
-
if (onChange) {
|
|
30
|
-
element.removeEventListener('change', handleChange);
|
|
31
|
-
}
|
|
32
|
-
if (onSearch) {
|
|
33
|
-
element.removeEventListener('search', handleSearch);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
}, [handleChange, handleSearch, onChange, onSearch]);
|
|
37
|
-
// Imperatively sync `value` to the underlying element's property whenever
|
|
38
|
-
// it changes. React 18's prop-to-property bridging for custom elements is
|
|
39
|
-
// unreliable for empty strings (programmatic resets), so we set the
|
|
40
|
-
// property directly to guarantee the dropdown clears on `value=""`.
|
|
41
|
-
// React 19+ handles this natively, so the effect short-circuits there.
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (!needsPropertyBridge)
|
|
44
|
-
return;
|
|
45
|
-
const element = elementRef.current;
|
|
46
|
-
if (!element)
|
|
47
|
-
return;
|
|
48
|
-
if (element.value !== value) {
|
|
49
|
-
element.value = value ?? '';
|
|
50
|
-
}
|
|
51
|
-
}, [value]);
|
|
52
|
-
// Handle ref forwarding
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (ref && elementRef.current) {
|
|
55
|
-
if (typeof ref === 'function') {
|
|
56
|
-
ref(elementRef.current);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
ref.current = elementRef.current;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}, [ref]);
|
|
63
|
-
// Render options from data if provided, otherwise use children
|
|
64
|
-
const renderContent = () => {
|
|
65
|
-
if (options && options.length > 0) {
|
|
66
|
-
// Data-driven approach - create ty-option elements
|
|
67
|
-
return options.map((option, index) => React.createElement('ty-option', {
|
|
68
|
-
key: option.value || index,
|
|
69
|
-
value: option.value,
|
|
70
|
-
...(option.disabled && { disabled: "" }),
|
|
71
|
-
}, option.text));
|
|
72
|
-
}
|
|
73
|
-
// Slotted approach - use provided children
|
|
74
|
-
return children;
|
|
75
|
-
};
|
|
76
|
-
// Imperative property sync for boolean props (see use-boolean-prop.ts).
|
|
77
|
-
const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
|
|
78
|
-
const isLoading = useBooleanProperty(elementRef, 'loading', loading);
|
|
79
|
-
const isExternalSearch = coerceBool(externalSearch) || coerceBool(notSearchable) || searchable === false;
|
|
80
|
-
useBooleanProperty(elementRef, 'externalSearch', isExternalSearch);
|
|
81
|
-
// clearable: explicit boolean OR the `notClearable` alias inverts it
|
|
82
|
-
const isClearable = clearable !== undefined
|
|
83
|
-
? coerceBool(clearable)
|
|
84
|
-
: (coerceBool(notClearable) ? false : undefined);
|
|
85
|
-
useEffect(() => {
|
|
86
|
-
if (!needsPropertyBridge)
|
|
87
|
-
return;
|
|
88
|
-
if (isClearable === undefined)
|
|
89
|
-
return;
|
|
90
|
-
const el = elementRef.current;
|
|
91
|
-
if (!el)
|
|
92
|
-
return;
|
|
93
|
-
if (Boolean(el.clearable) !== isClearable)
|
|
94
|
-
el.clearable = isClearable;
|
|
95
|
-
}, [isClearable]);
|
|
96
|
-
// Convert React props to web component attributes
|
|
97
|
-
const webComponentProps = {
|
|
98
|
-
...props,
|
|
99
|
-
ref: elementRef,
|
|
100
|
-
};
|
|
101
|
-
if (isDisabled)
|
|
102
|
-
webComponentProps.disabled = '';
|
|
103
|
-
if (isLoading)
|
|
104
|
-
webComponentProps.loading = '';
|
|
105
|
-
if (isExternalSearch)
|
|
106
|
-
webComponentProps['external-search'] = '';
|
|
107
|
-
// Handle clearable functionality
|
|
108
|
-
if (isClearable === true) {
|
|
109
|
-
webComponentProps.clearable = '';
|
|
110
|
-
}
|
|
111
|
-
else if (isClearable === false) {
|
|
112
|
-
webComponentProps['not-clearable'] = '';
|
|
113
|
-
}
|
|
114
|
-
// Add debounce attribute
|
|
115
|
-
if (debounce !== undefined) {
|
|
116
|
-
webComponentProps.debounce = debounce;
|
|
117
|
-
}
|
|
118
|
-
// Add string attributes
|
|
119
|
-
if (name)
|
|
120
|
-
webComponentProps.name = name;
|
|
121
|
-
return React.createElement('ty-dropdown', webComponentProps, renderContent());
|
|
122
|
-
});
|
|
123
|
-
TyDropdown.displayName = 'TyDropdown';
|
|
124
|
-
//# sourceMappingURL=TyDropdown.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TyDropdown.js","sourceRoot":"","sources":["../../src/components/TyDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAqF3E,8CAA8C;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CACxC,CAAC,EACC,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,cAAc,EACd,aAAa,EACb,UAAU,EACV,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,GAAG,KAAK,EACT,EAAE,GAAG,EAAE,EAAE;IACR,MAAM,UAAU,GAAG,MAAM,CAAc,IAAI,CAAC,CAAC;IAE7C,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,KAAyC,EAAE,EAAE;QAC7E,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,KAA2D,EAAE,EAAE;QAC/F,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;QACnC,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,4CAA4C;QAC5C,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAA6B,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAA6B,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,GAAG,EAAE;YACV,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAA6B,CAAC,CAAC;YACvE,CAAC;YACD,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAA6B,CAAC,CAAC;YACvE,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAErD,0EAA0E;IAC1E,0EAA0E;IAC1E,oEAAoE;IACpE,oEAAoE;IACpE,uEAAuE;IACvE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,mBAAmB;YAAE,OAAO;QACjC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAc,CAAC;QAC1C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,wBAAwB;IACxB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,GAAG,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE,CAAC;gBAC9B,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEV,+DAA+D;IAC/D,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,mDAAmD;YACnD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CACnC,KAAK,CAAC,aAAa,CACjB,WAAW,EACX;gBACE,GAAG,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK;gBAC1B,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;aACzC,EACD,MAAM,CAAC,IAAI,CACZ,CACF,CAAC;QACJ,CAAC;QAED,2CAA2C;QAC3C,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,wEAAwE;IACxE,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,gBAAgB,GAAG,UAAU,CAAC,cAAc,CAAC,IAAI,UAAU,CAAC,aAAa,CAAC,IAAI,UAAU,KAAK,KAAK,CAAC;IACzG,kBAAkB,CAAC,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;IACnE,qEAAqE;IACrE,MAAM,WAAW,GAAG,SAAS,KAAK,SAAS;QACzC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC;QACvB,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACnD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,mBAAmB;YAAE,OAAO;QACjC,IAAI,WAAW,KAAK,SAAS;YAAE,OAAO;QACtC,MAAM,EAAE,GAAG,UAAU,CAAC,OAAc,CAAC;QACrC,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,OAAO,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,WAAW;YAAE,EAAE,CAAC,SAAS,GAAG,WAAW,CAAC;IACxE,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,kDAAkD;IAClD,MAAM,iBAAiB,GAAwB;QAC7C,GAAG,KAAK;QACR,GAAG,EAAE,UAAU;KAChB,CAAC;IAEF,IAAI,UAAU;QAAE,iBAAiB,CAAC,QAAQ,GAAG,EAAE,CAAC;IAChD,IAAI,SAAS;QAAE,iBAAiB,CAAC,OAAO,GAAG,EAAE,CAAC;IAC9C,IAAI,gBAAgB;QAAE,iBAAiB,CAAC,iBAAiB,CAAC,GAAG,EAAE,CAAC;IAEhE,iCAAiC;IACjC,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACzB,iBAAiB,CAAC,SAAS,GAAG,EAAE,CAAC;IACnC,CAAC;SAAM,IAAI,WAAW,KAAK,KAAK,EAAE,CAAC;QACjC,iBAAiB,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC;IAC1C,CAAC;IAED,yBAAyB;IACzB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,iBAAiB,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACxC,CAAC;IAED,wBAAwB;IACxB,IAAI,IAAI;QAAE,iBAAiB,CAAC,IAAI,GAAG,IAAI,CAAC;IAExC,OAAO,KAAK,CAAC,aAAa,CACxB,aAAa,EACb,iBAAiB,EACjB,aAAa,EAAE,CAChB,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,UAAU,CAAC,WAAW,GAAG,YAAY,CAAC"}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export interface TyMultiselectEventDetail {
|
|
3
|
-
/** Array of currently selected values */
|
|
4
|
-
values: string[];
|
|
5
|
-
/** Action that triggered the change: "add" | "remove" */
|
|
6
|
-
action: 'add' | 'remove';
|
|
7
|
-
/** The specific item that was added or removed */
|
|
8
|
-
item: string;
|
|
9
|
-
}
|
|
10
|
-
export interface TyMultiselectProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onChange' | 'style'> {
|
|
11
|
-
style?: import('./TyInput').TyInputCSSProperties;
|
|
12
|
-
/** Current selected values as comma-separated string or array */
|
|
13
|
-
value?: string | string[];
|
|
14
|
-
/** Placeholder text when no items are selected */
|
|
15
|
-
placeholder?: string;
|
|
16
|
-
/** Disable the multiselect component */
|
|
17
|
-
disabled?: boolean;
|
|
18
|
-
/**
|
|
19
|
-
* Loading state — replaces the available-options area with a centered
|
|
20
|
-
* spinner. Search input stays usable. Pair with `externalSearch` while
|
|
21
|
-
* fetching results from a parent-owned data source.
|
|
22
|
-
*/
|
|
23
|
-
loading?: boolean;
|
|
24
|
-
/** Make the multiselect read-only */
|
|
25
|
-
readonly?: boolean;
|
|
26
|
-
/** Semantic styling variant */
|
|
27
|
-
flavor?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
|
|
28
|
-
/** Label text for the multiselect */
|
|
29
|
-
label?: string;
|
|
30
|
-
/** Mark the field as required */
|
|
31
|
-
required?: boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Switch to external (remote) search mode. Default is `false` — the
|
|
34
|
-
* multiselect filters its own children client-side. Set to `true` when you
|
|
35
|
-
* want to handle filtering yourself (e.g. server-side): the component will
|
|
36
|
-
* dispatch `search` events on each keystroke, and you must update the children
|
|
37
|
-
* in response. The `onSearch` prop receives those events.
|
|
38
|
-
*/
|
|
39
|
-
externalSearch?: boolean;
|
|
40
|
-
/** Debounce in milliseconds (0-5000) */
|
|
41
|
-
debounce?: number;
|
|
42
|
-
/** Mobile section label for selected items */
|
|
43
|
-
selectedLabel?: string;
|
|
44
|
-
/** Form field name for form submission */
|
|
45
|
-
name?: string;
|
|
46
|
-
/** Callback when selection changes */
|
|
47
|
-
onChange?: (event: CustomEvent<TyMultiselectEventDetail>) => void;
|
|
48
|
-
/** Callback fired on each search input change (debounced by `debounce`). Use for external/server-side filtering. */
|
|
49
|
-
onSearch?: (event: CustomEvent<{
|
|
50
|
-
query: string;
|
|
51
|
-
element: HTMLElement;
|
|
52
|
-
}>) => void;
|
|
53
|
-
/** Children should be TyTag components, not TyOption */
|
|
54
|
-
children?: React.ReactNode;
|
|
55
|
-
}
|
|
56
|
-
export declare const TyMultiselect: React.ForwardRefExoticComponent<TyMultiselectProps & React.RefAttributes<HTMLElement>>;
|
|
57
|
-
//# sourceMappingURL=TyMultiselect.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TyMultiselect.d.ts","sourceRoot":"","sources":["../../src/components/TyMultiselect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAK9D,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,yDAAyD;IACzD,MAAM,EAAE,KAAK,GAAG,QAAQ,CAAC;IACzB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC;IACvG,KAAK,CAAC,EAAE,OAAO,WAAW,EAAE,oBAAoB,CAAC;IACjD,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAE1B,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAEhF,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,sCAAsC;IACtC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,wBAAwB,CAAC,KAAK,IAAI,CAAC;IAElE,oHAAoH;IACpH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IAEjF,wDAAwD;IACxD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAGD,eAAO,MAAM,aAAa,wFAoIzB,CAAC"}
|
|
@@ -1,111 +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
|
-
// React wrapper for ty-multiselect web component
|
|
5
|
-
export const TyMultiselect = React.forwardRef(({ value, placeholder, disabled, loading, readonly, flavor, label, required, externalSearch, debounce, selectedLabel, name, onChange, onSearch, children, ...props }, ref) => {
|
|
6
|
-
const elementRef = useRef(null);
|
|
7
|
-
// Handle ref forwarding
|
|
8
|
-
useEffect(() => {
|
|
9
|
-
if (ref && elementRef.current) {
|
|
10
|
-
if (typeof ref === 'function') {
|
|
11
|
-
ref(elementRef.current);
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
ref.current = elementRef.current;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}, [ref]);
|
|
18
|
-
// Imperatively sync `value` to the underlying property so resets
|
|
19
|
-
// (`value=""` or null) reliably clear the visible selection.
|
|
20
|
-
// React 18 workaround; React 19+ handles this natively.
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (!needsPropertyBridge)
|
|
23
|
-
return;
|
|
24
|
-
const element = elementRef.current;
|
|
25
|
-
if (!element)
|
|
26
|
-
return;
|
|
27
|
-
const next = props.value ?? '';
|
|
28
|
-
if (element.value !== next) {
|
|
29
|
-
element.value = next;
|
|
30
|
-
}
|
|
31
|
-
}, [props.value]);
|
|
32
|
-
// Handle change events
|
|
33
|
-
const handleChange = useCallback((event) => {
|
|
34
|
-
const customEvent = event;
|
|
35
|
-
if (onChange) {
|
|
36
|
-
onChange(customEvent);
|
|
37
|
-
}
|
|
38
|
-
}, [onChange]);
|
|
39
|
-
const handleSearch = useCallback((event) => {
|
|
40
|
-
const customEvent = event;
|
|
41
|
-
if (onSearch) {
|
|
42
|
-
onSearch(customEvent);
|
|
43
|
-
}
|
|
44
|
-
}, [onSearch]);
|
|
45
|
-
// Set up event listeners
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
const element = elementRef.current;
|
|
48
|
-
if (!element)
|
|
49
|
-
return;
|
|
50
|
-
if (onChange)
|
|
51
|
-
element.addEventListener('change', handleChange);
|
|
52
|
-
if (onSearch)
|
|
53
|
-
element.addEventListener('search', handleSearch);
|
|
54
|
-
return () => {
|
|
55
|
-
if (onChange)
|
|
56
|
-
element.removeEventListener('change', handleChange);
|
|
57
|
-
if (onSearch)
|
|
58
|
-
element.removeEventListener('search', handleSearch);
|
|
59
|
-
};
|
|
60
|
-
}, [handleChange, handleSearch, onChange, onSearch]);
|
|
61
|
-
// Imperative property sync for boolean props (see use-boolean-prop.ts).
|
|
62
|
-
const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
|
|
63
|
-
const isLoading = useBooleanProperty(elementRef, 'loading', loading);
|
|
64
|
-
const isReadonly = useBooleanProperty(elementRef, 'readonly', readonly);
|
|
65
|
-
const isRequired = useBooleanProperty(elementRef, 'required', required);
|
|
66
|
-
const isExternalSearch = useBooleanProperty(elementRef, 'externalSearch', externalSearch);
|
|
67
|
-
// Convert React props to web component attributes
|
|
68
|
-
const webComponentProps = {
|
|
69
|
-
...props,
|
|
70
|
-
ref: elementRef,
|
|
71
|
-
};
|
|
72
|
-
// Handle value conversion (array to comma-separated string)
|
|
73
|
-
if (value !== undefined) {
|
|
74
|
-
const valueString = Array.isArray(value) ? value.join(',') : value;
|
|
75
|
-
webComponentProps.value = valueString;
|
|
76
|
-
}
|
|
77
|
-
// Add optional attributes only if they have values
|
|
78
|
-
if (placeholder) {
|
|
79
|
-
webComponentProps.placeholder = placeholder;
|
|
80
|
-
}
|
|
81
|
-
if (isLoading)
|
|
82
|
-
webComponentProps.loading = '';
|
|
83
|
-
if (isDisabled)
|
|
84
|
-
webComponentProps.disabled = '';
|
|
85
|
-
if (isReadonly)
|
|
86
|
-
webComponentProps.readonly = '';
|
|
87
|
-
if (isRequired)
|
|
88
|
-
webComponentProps.required = '';
|
|
89
|
-
if (isExternalSearch)
|
|
90
|
-
webComponentProps['external-search'] = '';
|
|
91
|
-
if (flavor) {
|
|
92
|
-
webComponentProps.flavor = flavor;
|
|
93
|
-
}
|
|
94
|
-
if (label) {
|
|
95
|
-
webComponentProps.label = label;
|
|
96
|
-
}
|
|
97
|
-
if (name) {
|
|
98
|
-
webComponentProps.name = name;
|
|
99
|
-
}
|
|
100
|
-
// Add debounce attribute
|
|
101
|
-
if (debounce !== undefined) {
|
|
102
|
-
webComponentProps.debounce = debounce;
|
|
103
|
-
}
|
|
104
|
-
// Add selectedLabel attribute
|
|
105
|
-
if (selectedLabel) {
|
|
106
|
-
webComponentProps['selected-label'] = selectedLabel;
|
|
107
|
-
}
|
|
108
|
-
return React.createElement('ty-multiselect', webComponentProps, children);
|
|
109
|
-
});
|
|
110
|
-
TyMultiselect.displayName = 'TyMultiselect';
|
|
111
|
-
//# sourceMappingURL=TyMultiselect.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TyMultiselect.js","sourceRoot":"","sources":["../../src/components/TyMultiselect.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test Example: React Event Convention
|
|
3
|
-
*
|
|
4
|
-
* This file demonstrates the new event handling convention
|
|
5
|
-
* for tyrell-react components.
|
|
6
|
-
*
|
|
7
|
-
* To test:
|
|
8
|
-
* 1. Create a new React project or use existing
|
|
9
|
-
* 2. npm install tyrell-react
|
|
10
|
-
* 3. Add this component to your app
|
|
11
|
-
* 4. Observe console logs and state updates
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import React, { useState } from 'react';
|
|
15
|
-
import { TyInput } from './TyInput';
|
|
16
|
-
import { TyTextarea } from './TyTextarea';
|
|
17
|
-
import { TyCheckbox } from './TyCheckbox';
|
|
18
|
-
import type { TyInputEventDetail } from './TyInput';
|
|
19
|
-
import type { TyTextareaEventDetail } from './TyTextarea';
|
|
20
|
-
import type { TyCheckboxEventDetail } from './TyCheckbox';
|
|
21
|
-
|
|
22
|
-
export function EventConventionTest() {
|
|
23
|
-
const [inputValue, setInputValue] = useState('');
|
|
24
|
-
const [textareaValue, setTextareaValue] = useState('');
|
|
25
|
-
const [checked, setChecked] = useState(false);
|
|
26
|
-
const [logs, setLogs] = useState<string[]>([]);
|
|
27
|
-
|
|
28
|
-
const addLog = (message: string) => {
|
|
29
|
-
setLogs(prev => [...prev, `${new Date().toLocaleTimeString()}: ${message}`]);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div className="p-8 space-y-6 max-w-2xl">
|
|
34
|
-
<h1 className="text-2xl font-bold">React Event Convention Test</h1>
|
|
35
|
-
|
|
36
|
-
{/* Input Test */}
|
|
37
|
-
<div className="space-y-2">
|
|
38
|
-
<h2 className="text-lg font-semibold">TyInput Test</h2>
|
|
39
|
-
<TyInput
|
|
40
|
-
label="Email"
|
|
41
|
-
placeholder="Type to test onChange..."
|
|
42
|
-
value={inputValue}
|
|
43
|
-
onChange={(e: CustomEvent<TyInputEventDetail>) => {
|
|
44
|
-
setInputValue(e.detail.value);
|
|
45
|
-
addLog(`onChange: "${e.detail.value}" (fires on keystroke)`);
|
|
46
|
-
}}
|
|
47
|
-
onChangeCommit={(e: CustomEvent<TyInputEventDetail>) => {
|
|
48
|
-
addLog(`onChangeCommit: "${e.detail.value}" (fires on blur)`);
|
|
49
|
-
}}
|
|
50
|
-
onFocus={() => addLog('onFocus')}
|
|
51
|
-
onBlur={() => addLog('onBlur')}
|
|
52
|
-
/>
|
|
53
|
-
<p className="text-sm text-gray-600">
|
|
54
|
-
Current value: <strong>{inputValue}</strong>
|
|
55
|
-
</p>
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
{/* Textarea Test */}
|
|
59
|
-
<div className="space-y-2">
|
|
60
|
-
<h2 className="text-lg font-semibold">TyTextarea Test</h2>
|
|
61
|
-
<TyTextarea
|
|
62
|
-
label="Comments"
|
|
63
|
-
placeholder="Type to test onChange..."
|
|
64
|
-
value={textareaValue}
|
|
65
|
-
rows={3}
|
|
66
|
-
onChange={(e: CustomEvent<TyTextareaEventDetail>) => {
|
|
67
|
-
setTextareaValue(e.detail.value);
|
|
68
|
-
addLog(`Textarea onChange: "${e.detail.value}"`);
|
|
69
|
-
}}
|
|
70
|
-
onChangeCommit={(e: CustomEvent<TyTextareaEventDetail>) => {
|
|
71
|
-
addLog(`Textarea onChangeCommit: "${e.detail.value}"`);
|
|
72
|
-
}}
|
|
73
|
-
/>
|
|
74
|
-
<p className="text-sm text-gray-600">
|
|
75
|
-
Current value: <strong>{textareaValue}</strong>
|
|
76
|
-
</p>
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
{/* Checkbox Test */}
|
|
80
|
-
<div className="space-y-2">
|
|
81
|
-
<h2 className="text-lg font-semibold">TyCheckbox Test</h2>
|
|
82
|
-
<TyCheckbox
|
|
83
|
-
checked={checked}
|
|
84
|
-
onChange={(e: CustomEvent<TyCheckboxEventDetail>) => {
|
|
85
|
-
setChecked(e.detail.checked);
|
|
86
|
-
addLog(`Checkbox onChange: ${e.detail.checked} (fires immediately)`);
|
|
87
|
-
}}
|
|
88
|
-
onChangeCommit={(e: CustomEvent<TyCheckboxEventDetail>) => {
|
|
89
|
-
addLog(`Checkbox onChangeCommit: ${e.detail.checked} (fires on blur)`);
|
|
90
|
-
}}
|
|
91
|
-
>
|
|
92
|
-
Subscribe to newsletter
|
|
93
|
-
</TyCheckbox>
|
|
94
|
-
<p className="text-sm text-gray-600">
|
|
95
|
-
Current state: <strong>{checked ? 'Checked' : 'Unchecked'}</strong>
|
|
96
|
-
</p>
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
{/* Event Log */}
|
|
100
|
-
<div className="space-y-2">
|
|
101
|
-
<h2 className="text-lg font-semibold">Event Log</h2>
|
|
102
|
-
<div className="bg-gray-100 p-4 rounded max-h-64 overflow-y-auto font-mono text-xs">
|
|
103
|
-
{logs.length === 0 ? (
|
|
104
|
-
<p className="text-gray-500">No events yet. Start typing or checking boxes!</p>
|
|
105
|
-
) : (
|
|
106
|
-
logs.map((log, i) => (
|
|
107
|
-
<div key={i} className="text-gray-800">
|
|
108
|
-
{log}
|
|
109
|
-
</div>
|
|
110
|
-
))
|
|
111
|
-
)}
|
|
112
|
-
</div>
|
|
113
|
-
<button
|
|
114
|
-
onClick={() => setLogs([])}
|
|
115
|
-
className="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300"
|
|
116
|
-
>
|
|
117
|
-
Clear Log
|
|
118
|
-
</button>
|
|
119
|
-
</div>
|
|
120
|
-
|
|
121
|
-
{/* Expected Behavior */}
|
|
122
|
-
<div className="bg-blue-50 p-4 rounded">
|
|
123
|
-
<h3 className="font-semibold mb-2">Expected Behavior:</h3>
|
|
124
|
-
<ul className="list-disc list-inside space-y-1 text-sm">
|
|
125
|
-
<li>
|
|
126
|
-
<strong>onChange</strong> - Fires on every keystroke/state change (React convention)
|
|
127
|
-
</li>
|
|
128
|
-
<li>
|
|
129
|
-
<strong>onChangeCommit</strong> - Fires on blur if value changed (optional)
|
|
130
|
-
</li>
|
|
131
|
-
<li>
|
|
132
|
-
<strong>onFocus</strong> - Fires when element gains focus
|
|
133
|
-
</li>
|
|
134
|
-
<li>
|
|
135
|
-
<strong>onBlur</strong> - Fires when element loses focus
|
|
136
|
-
</li>
|
|
137
|
-
</ul>
|
|
138
|
-
</div>
|
|
139
|
-
|
|
140
|
-
{/* Verification */}
|
|
141
|
-
<div className="bg-green-50 p-4 rounded">
|
|
142
|
-
<h3 className="font-semibold mb-2">Verification Checklist:</h3>
|
|
143
|
-
<ul className="list-disc list-inside space-y-1 text-sm">
|
|
144
|
-
<li>✅ onChange fires on EVERY keystroke (not just on blur)</li>
|
|
145
|
-
<li>✅ State updates in real-time as you type</li>
|
|
146
|
-
<li>✅ onChangeCommit fires ONLY on blur (if value changed)</li>
|
|
147
|
-
<li>✅ Event order: onChange → onBlur → onChangeCommit</li>
|
|
148
|
-
<li>✅ Checkbox onChange fires immediately on click</li>
|
|
149
|
-
</ul>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export default EventConventionTest;
|