tyrell-react 1.0.0-RC10
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/LICENSE +21 -0
- package/README.md +410 -0
- package/dist/components/TyButton.d.ts +52 -0
- package/dist/components/TyButton.d.ts.map +1 -0
- package/dist/components/TyButton.js +76 -0
- package/dist/components/TyButton.js.map +1 -0
- package/dist/components/TyCalendar.d.ts +63 -0
- package/dist/components/TyCalendar.d.ts.map +1 -0
- package/dist/components/TyCalendar.js +128 -0
- package/dist/components/TyCalendar.js.map +1 -0
- package/dist/components/TyCalendarMonth.d.ts +32 -0
- package/dist/components/TyCalendarMonth.d.ts.map +1 -0
- package/dist/components/TyCalendarMonth.js +54 -0
- package/dist/components/TyCalendarMonth.js.map +1 -0
- package/dist/components/TyCalendarNavigation.d.ts +21 -0
- package/dist/components/TyCalendarNavigation.d.ts.map +1 -0
- package/dist/components/TyCalendarNavigation.js +50 -0
- package/dist/components/TyCalendarNavigation.js.map +1 -0
- package/dist/components/TyCheckbox.d.ts +39 -0
- package/dist/components/TyCheckbox.d.ts.map +1 -0
- package/dist/components/TyCheckbox.js +76 -0
- package/dist/components/TyCheckbox.js.map +1 -0
- package/dist/components/TyCopy.d.ts +21 -0
- package/dist/components/TyCopy.d.ts.map +1 -0
- package/dist/components/TyCopy.js +46 -0
- package/dist/components/TyCopy.js.map +1 -0
- package/dist/components/TyDatePicker.d.ts +45 -0
- package/dist/components/TyDatePicker.d.ts.map +1 -0
- package/dist/components/TyDatePicker.js +122 -0
- package/dist/components/TyDatePicker.js.map +1 -0
- package/dist/components/TyDropdown.d.ts +62 -0
- package/dist/components/TyDropdown.d.ts.map +1 -0
- package/dist/components/TyDropdown.js +124 -0
- package/dist/components/TyDropdown.js.map +1 -0
- package/dist/components/TyFileUpload.d.ts +31 -0
- package/dist/components/TyFileUpload.d.ts.map +1 -0
- package/dist/components/TyFileUpload.js +56 -0
- package/dist/components/TyFileUpload.js.map +1 -0
- package/dist/components/TyIcon.d.ts +17 -0
- package/dist/components/TyIcon.d.ts.map +1 -0
- package/dist/components/TyIcon.js +42 -0
- package/dist/components/TyIcon.js.map +1 -0
- package/dist/components/TyInput.d.ts +65 -0
- package/dist/components/TyInput.d.ts.map +1 -0
- package/dist/components/TyInput.js +134 -0
- package/dist/components/TyInput.js.map +1 -0
- package/dist/components/TyModal.d.ts +48 -0
- package/dist/components/TyModal.d.ts.map +1 -0
- package/dist/components/TyModal.js +120 -0
- package/dist/components/TyModal.js.map +1 -0
- package/dist/components/TyMultiselect.d.ts +57 -0
- package/dist/components/TyMultiselect.d.ts.map +1 -0
- package/dist/components/TyMultiselect.js +111 -0
- package/dist/components/TyMultiselect.js.map +1 -0
- package/dist/components/TyOption.d.ts +10 -0
- package/dist/components/TyOption.d.ts.map +1 -0
- package/dist/components/TyOption.js +29 -0
- package/dist/components/TyOption.js.map +1 -0
- package/dist/components/TyPopup.d.ts +24 -0
- package/dist/components/TyPopup.d.ts.map +1 -0
- package/dist/components/TyPopup.js +70 -0
- package/dist/components/TyPopup.js.map +1 -0
- package/dist/components/TyRadio.d.ts +20 -0
- package/dist/components/TyRadio.d.ts.map +1 -0
- package/dist/components/TyRadio.js +35 -0
- package/dist/components/TyRadio.js.map +1 -0
- package/dist/components/TyRadioGroup.d.ts +40 -0
- package/dist/components/TyRadioGroup.d.ts.map +1 -0
- package/dist/components/TyRadioGroup.js +61 -0
- package/dist/components/TyRadioGroup.js.map +1 -0
- package/dist/components/TyResizeObserver.d.ts +11 -0
- package/dist/components/TyResizeObserver.d.ts.map +1 -0
- package/dist/components/TyResizeObserver.js +28 -0
- package/dist/components/TyResizeObserver.js.map +1 -0
- package/dist/components/TyScrollContainer.d.ts +25 -0
- package/dist/components/TyScrollContainer.d.ts.map +1 -0
- package/dist/components/TyScrollContainer.js +61 -0
- package/dist/components/TyScrollContainer.js.map +1 -0
- package/dist/components/TyStep.d.ts +17 -0
- package/dist/components/TyStep.d.ts.map +1 -0
- package/dist/components/TyStep.js +35 -0
- package/dist/components/TyStep.js.map +1 -0
- package/dist/components/TySwitch.d.ts +35 -0
- package/dist/components/TySwitch.d.ts.map +1 -0
- package/dist/components/TySwitch.js +59 -0
- package/dist/components/TySwitch.js.map +1 -0
- package/dist/components/TyTab.d.ts +13 -0
- package/dist/components/TyTab.d.ts.map +1 -0
- package/dist/components/TyTab.js +34 -0
- package/dist/components/TyTab.js.map +1 -0
- package/dist/components/TyTabs.d.ts +23 -0
- package/dist/components/TyTabs.d.ts.map +1 -0
- package/dist/components/TyTabs.js +48 -0
- package/dist/components/TyTabs.js.map +1 -0
- package/dist/components/TyTag.d.ts +22 -0
- package/dist/components/TyTag.d.ts.map +1 -0
- package/dist/components/TyTag.js +51 -0
- package/dist/components/TyTag.js.map +1 -0
- package/dist/components/TyTextarea.d.ts +37 -0
- package/dist/components/TyTextarea.d.ts.map +1 -0
- package/dist/components/TyTextarea.js +116 -0
- package/dist/components/TyTextarea.js.map +1 -0
- package/dist/components/TyTooltip.d.ts +17 -0
- package/dist/components/TyTooltip.d.ts.map +1 -0
- package/dist/components/TyTooltip.js +41 -0
- package/dist/components/TyTooltip.js.map +1 -0
- package/dist/components/TyWizard.d.ts +26 -0
- package/dist/components/TyWizard.d.ts.map +1 -0
- package/dist/components/TyWizard.js +50 -0
- package/dist/components/TyWizard.js.map +1 -0
- package/dist/components/index.d.ts +112 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +127 -0
- package/dist/components/index.js.map +1 -0
- package/dist/utils/react-version.d.ts +2 -0
- package/dist/utils/react-version.d.ts.map +1 -0
- package/dist/utils/react-version.js +8 -0
- package/dist/utils/react-version.js.map +1 -0
- package/dist/utils/use-boolean-prop.d.ts +36 -0
- package/dist/utils/use-boolean-prop.d.ts.map +1 -0
- package/dist/utils/use-boolean-prop.js +62 -0
- package/dist/utils/use-boolean-prop.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -0
- package/src/components/EventConventionTest.tsx +155 -0
- package/src/components/TyButton.tsx +157 -0
- package/src/components/TyCalendar.tsx +247 -0
- package/src/components/TyCalendarMonth.tsx +108 -0
- package/src/components/TyCalendarNavigation.tsx +91 -0
- package/src/components/TyCheckbox.tsx +147 -0
- package/src/components/TyCopy.tsx +83 -0
- package/src/components/TyDatePicker.tsx +215 -0
- package/src/components/TyDropdown.tsx +240 -0
- package/src/components/TyFileUpload.tsx +108 -0
- package/src/components/TyIcon.tsx +71 -0
- package/src/components/TyInput.tsx +239 -0
- package/src/components/TyModal.tsx +195 -0
- package/src/components/TyMultiselect.tsx +208 -0
- package/src/components/TyOption.tsx +47 -0
- package/src/components/TyPopup.tsx +116 -0
- package/src/components/TyRadio.tsx +61 -0
- package/src/components/TyRadioGroup.tsx +125 -0
- package/src/components/TyResizeObserver.tsx +54 -0
- package/src/components/TyScrollContainer.tsx +102 -0
- package/src/components/TyStep.tsx +71 -0
- package/src/components/TySwitch.tsx +114 -0
- package/src/components/TyTab.tsx +65 -0
- package/src/components/TyTabs.tsx +93 -0
- package/src/components/TyTag.tsx +86 -0
- package/src/components/TyTextarea.tsx +181 -0
- package/src/components/TyTooltip.tsx +83 -0
- package/src/components/TyWizard.tsx +99 -0
- package/src/components/index.ts +279 -0
- package/src/utils/react-version.ts +8 -0
- package/src/utils/use-boolean-prop.ts +62 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// ===================================================================
|
|
2
|
+
// VERSION
|
|
3
|
+
// ===================================================================
|
|
4
|
+
import { VERSION } from '../version';
|
|
5
|
+
/** Current version of tyrell-react (auto-synced with package.json on build). */
|
|
6
|
+
export { VERSION };
|
|
7
|
+
// One-time load banner so consumers can confirm which version of
|
|
8
|
+
// tyrell-react their page is actually running. Mirrors the banner in
|
|
9
|
+
// tyrell-components. Filter DevTools with "tyrell-react" if noisy.
|
|
10
|
+
if (typeof window !== 'undefined') {
|
|
11
|
+
window.tyReactVersion = VERSION;
|
|
12
|
+
console.log(`%c[tyrell-react]%c v${VERSION}`, 'color:#a78bfa;font-weight:600', 'color:inherit;font-weight:400');
|
|
13
|
+
}
|
|
14
|
+
// ===================================================================
|
|
15
|
+
// TYRELL REACT WRAPPER EXPORTS
|
|
16
|
+
// ===================================================================
|
|
17
|
+
// This file provides two export styles for maximum developer flexibility:
|
|
18
|
+
// 1. Ty-prefixed exports (TyButton, TyInput) - explicit and backward compatible
|
|
19
|
+
// 2. Short name exports (Button, Input) - clean and familiar to React developers
|
|
20
|
+
//
|
|
21
|
+
// Choose the style that fits your team's preferences!
|
|
22
|
+
// ===================================================================
|
|
23
|
+
// TY-PREFIXED EXPORTS (Explicit Style - Backward Compatible)
|
|
24
|
+
// ===================================================================
|
|
25
|
+
export { TyButton } from './TyButton';
|
|
26
|
+
export { TyTag } from './TyTag';
|
|
27
|
+
export { TyInput } from './TyInput';
|
|
28
|
+
export { TyTextarea } from './TyTextarea';
|
|
29
|
+
export { TyDropdown } from './TyDropdown';
|
|
30
|
+
export { TyOption } from './TyOption';
|
|
31
|
+
export { TyIcon } from './TyIcon';
|
|
32
|
+
export { TyModal } from './TyModal';
|
|
33
|
+
export { TyTooltip } from './TyTooltip';
|
|
34
|
+
export { TyMultiselect } from './TyMultiselect';
|
|
35
|
+
export { TyCalendar } from './TyCalendar';
|
|
36
|
+
export { TyDatePicker } from './TyDatePicker';
|
|
37
|
+
export { TyPopup } from './TyPopup';
|
|
38
|
+
export { TyCheckbox } from './TyCheckbox';
|
|
39
|
+
export { TySwitch } from './TySwitch';
|
|
40
|
+
export { TyRadio } from './TyRadio';
|
|
41
|
+
export { TyRadioGroup } from './TyRadioGroup';
|
|
42
|
+
export { TyCopy } from './TyCopy';
|
|
43
|
+
export { TyFileUpload } from './TyFileUpload';
|
|
44
|
+
export { TyTabs } from './TyTabs';
|
|
45
|
+
export { TyTab } from './TyTab';
|
|
46
|
+
export { TyCalendarMonth } from './TyCalendarMonth';
|
|
47
|
+
export { TyCalendarNavigation } from './TyCalendarNavigation';
|
|
48
|
+
export { TyWizard } from './TyWizard';
|
|
49
|
+
export { TyStep } from './TyStep';
|
|
50
|
+
export { TyResizeObserver } from './TyResizeObserver';
|
|
51
|
+
export { TyScrollContainer } from './TyScrollContainer';
|
|
52
|
+
// ===================================================================
|
|
53
|
+
// SHORT NAME EXPORTS (Clean Style - Developer Choice)
|
|
54
|
+
// ===================================================================
|
|
55
|
+
export { TyButton as Button } from './TyButton';
|
|
56
|
+
export { TyTag as Tag } from './TyTag';
|
|
57
|
+
export { TyInput as Input } from './TyInput';
|
|
58
|
+
export { TyTextarea as Textarea } from './TyTextarea';
|
|
59
|
+
export { TyDropdown as Dropdown } from './TyDropdown';
|
|
60
|
+
export { TyOption as Option } from './TyOption';
|
|
61
|
+
export { TyIcon as Icon } from './TyIcon';
|
|
62
|
+
export { TyModal as Modal } from './TyModal';
|
|
63
|
+
export { TyTooltip as Tooltip } from './TyTooltip';
|
|
64
|
+
export { TyMultiselect as Multiselect } from './TyMultiselect';
|
|
65
|
+
export { TyCalendar as Calendar } from './TyCalendar';
|
|
66
|
+
export { TyDatePicker as DatePicker } from './TyDatePicker';
|
|
67
|
+
export { TyPopup as Popup } from './TyPopup';
|
|
68
|
+
export { TyCheckbox as Checkbox } from './TyCheckbox';
|
|
69
|
+
export { TySwitch as Switch } from './TySwitch';
|
|
70
|
+
export { TyRadio as Radio } from './TyRadio';
|
|
71
|
+
export { TyRadioGroup as RadioGroup } from './TyRadioGroup';
|
|
72
|
+
export { TyCopy as Copy } from './TyCopy';
|
|
73
|
+
export { TyFileUpload as FileUpload } from './TyFileUpload';
|
|
74
|
+
export { TyTabs as Tabs } from './TyTabs';
|
|
75
|
+
export { TyTab as Tab } from './TyTab';
|
|
76
|
+
export { TyCalendarMonth as CalendarMonth } from './TyCalendarMonth';
|
|
77
|
+
export { TyCalendarNavigation as CalendarNavigation } from './TyCalendarNavigation';
|
|
78
|
+
export { TyWizard as Wizard } from './TyWizard';
|
|
79
|
+
export { TyStep as Step } from './TyStep';
|
|
80
|
+
export { TyResizeObserver as ResizeObserver } from './TyResizeObserver';
|
|
81
|
+
export { TyScrollContainer as ScrollContainer } from './TyScrollContainer';
|
|
82
|
+
// ===================================================================
|
|
83
|
+
// USAGE EXAMPLES
|
|
84
|
+
// ===================================================================
|
|
85
|
+
/*
|
|
86
|
+
|
|
87
|
+
// STYLE 1: Ty-prefixed (Explicit and backward compatible)
|
|
88
|
+
import { TyButton, TyInput, TyModal } from 'tyrell-react';
|
|
89
|
+
import type { TyButtonProps, TyInputProps } from 'tyrell-react';
|
|
90
|
+
|
|
91
|
+
function MyComponent() {
|
|
92
|
+
return (
|
|
93
|
+
<TyModal>
|
|
94
|
+
<TyInput placeholder="Enter text..." />
|
|
95
|
+
<TyButton>Submit</TyButton>
|
|
96
|
+
</TyModal>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// STYLE 2: Short names (Clean and familiar)
|
|
101
|
+
import { Button, Input, Modal } from 'tyrell-react';
|
|
102
|
+
import type { ButtonProps, InputProps } from 'tyrell-react';
|
|
103
|
+
|
|
104
|
+
function MyComponent() {
|
|
105
|
+
return (
|
|
106
|
+
<Modal>
|
|
107
|
+
<Input placeholder="Enter text..." />
|
|
108
|
+
<Button>Submit</Button>
|
|
109
|
+
</Modal>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// STYLE 3: Mixed (Team preferences)
|
|
114
|
+
import { TyModal, Input, Button } from 'tyrell-react';
|
|
115
|
+
import type { TyModalProps, InputProps } from 'tyrell-react';
|
|
116
|
+
|
|
117
|
+
function MyComponent() {
|
|
118
|
+
return (
|
|
119
|
+
<TyModal>
|
|
120
|
+
<Input placeholder="Enter text..." />
|
|
121
|
+
<Button>Submit</Button>
|
|
122
|
+
</TyModal>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
*/
|
|
127
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-version.d.ts","sourceRoot":"","sources":["../../src/utils/react-version.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,mBAAmB,SAAmC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
// React 19 natively bridges props to custom-element properties (including
|
|
3
|
+
// empty strings, `false` booleans, functions, and objects). On React 18 we
|
|
4
|
+
// must imperatively assign these properties via useEffect. Wrappers gate
|
|
5
|
+
// their bridging effects on this flag so the workaround is dead code on
|
|
6
|
+
// React 19+ and removable when React 18 support is eventually dropped.
|
|
7
|
+
export const needsPropertyBridge = parseInt(React.version, 10) < 19;
|
|
8
|
+
//# sourceMappingURL=react-version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-version.js","sourceRoot":"","sources":["../../src/utils/react-version.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,0EAA0E;AAC1E,2EAA2E;AAC3E,yEAAyE;AACzE,wEAAwE;AACxE,uEAAuE;AACvE,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Coerce a value to a boolean using the same rules as the core property
|
|
4
|
+
* manager (packages/core/src/utils/property-manager.ts:142-152). This matters
|
|
5
|
+
* because consumers sometimes pass the string "false" through untyped call
|
|
6
|
+
* sites (JSON config, query params, server-rendered props) — and the JS
|
|
7
|
+
* `Boolean("false")` is surprisingly `true`.
|
|
8
|
+
*
|
|
9
|
+
* undefined | null | false | "false" | "0" → false
|
|
10
|
+
* "" → true (HTML boolean-attribute convention)
|
|
11
|
+
* any other truthy → true
|
|
12
|
+
*/
|
|
13
|
+
export declare function coerceBool(value: unknown): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Imperatively keep a boolean property on the underlying custom element in
|
|
16
|
+
* sync with its React prop.
|
|
17
|
+
*
|
|
18
|
+
* Why this exists: React 18 sets boolean attributes as empty strings on the
|
|
19
|
+
* first render to a custom element but does not reliably *remove* them when
|
|
20
|
+
* the prop flips back to `false`. Without this bridge, `<TyModal open>` →
|
|
21
|
+
* `<TyModal open={false}>` leaves the `open` attribute on the element and
|
|
22
|
+
* the modal stays open. Same class of bug affects `disabled`, `required`,
|
|
23
|
+
* `clearable`, `loading`, `readonly`, `protected`, etc.
|
|
24
|
+
*
|
|
25
|
+
* React 19+ bridges this natively — the effect short-circuits via
|
|
26
|
+
* `needsPropertyBridge`, so this is dead code on modern React.
|
|
27
|
+
*
|
|
28
|
+
* Pass the *camelCase JS property name* on the element (e.g. `externalSearch`
|
|
29
|
+
* for the `external-search` attribute). The core base class handles the
|
|
30
|
+
* attribute-side sync once the property changes.
|
|
31
|
+
*
|
|
32
|
+
* Returns the coerced boolean so the caller can also drive its conditional
|
|
33
|
+
* JSX attribute emission with a value that correctly handles "false".
|
|
34
|
+
*/
|
|
35
|
+
export declare function useBooleanProperty(ref: RefObject<HTMLElement | null>, propName: string, value: unknown): boolean;
|
|
36
|
+
//# sourceMappingURL=use-boolean-prop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-boolean-prop.d.ts","sourceRoot":"","sources":["../../src/utils/use-boolean-prop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAG7C;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASlD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,GACb,OAAO,CAWT"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { needsPropertyBridge } from './react-version';
|
|
3
|
+
/**
|
|
4
|
+
* Coerce a value to a boolean using the same rules as the core property
|
|
5
|
+
* manager (packages/core/src/utils/property-manager.ts:142-152). This matters
|
|
6
|
+
* because consumers sometimes pass the string "false" through untyped call
|
|
7
|
+
* sites (JSON config, query params, server-rendered props) — and the JS
|
|
8
|
+
* `Boolean("false")` is surprisingly `true`.
|
|
9
|
+
*
|
|
10
|
+
* undefined | null | false | "false" | "0" → false
|
|
11
|
+
* "" → true (HTML boolean-attribute convention)
|
|
12
|
+
* any other truthy → true
|
|
13
|
+
*/
|
|
14
|
+
export function coerceBool(value) {
|
|
15
|
+
if (value === undefined || value === null || value === false)
|
|
16
|
+
return false;
|
|
17
|
+
if (typeof value === 'string') {
|
|
18
|
+
if (value === '')
|
|
19
|
+
return true;
|
|
20
|
+
const norm = value.toLowerCase().trim();
|
|
21
|
+
if (norm === 'false' || norm === '0')
|
|
22
|
+
return false;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return Boolean(value);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Imperatively keep a boolean property on the underlying custom element in
|
|
29
|
+
* sync with its React prop.
|
|
30
|
+
*
|
|
31
|
+
* Why this exists: React 18 sets boolean attributes as empty strings on the
|
|
32
|
+
* first render to a custom element but does not reliably *remove* them when
|
|
33
|
+
* the prop flips back to `false`. Without this bridge, `<TyModal open>` →
|
|
34
|
+
* `<TyModal open={false}>` leaves the `open` attribute on the element and
|
|
35
|
+
* the modal stays open. Same class of bug affects `disabled`, `required`,
|
|
36
|
+
* `clearable`, `loading`, `readonly`, `protected`, etc.
|
|
37
|
+
*
|
|
38
|
+
* React 19+ bridges this natively — the effect short-circuits via
|
|
39
|
+
* `needsPropertyBridge`, so this is dead code on modern React.
|
|
40
|
+
*
|
|
41
|
+
* Pass the *camelCase JS property name* on the element (e.g. `externalSearch`
|
|
42
|
+
* for the `external-search` attribute). The core base class handles the
|
|
43
|
+
* attribute-side sync once the property changes.
|
|
44
|
+
*
|
|
45
|
+
* Returns the coerced boolean so the caller can also drive its conditional
|
|
46
|
+
* JSX attribute emission with a value that correctly handles "false".
|
|
47
|
+
*/
|
|
48
|
+
export function useBooleanProperty(ref, propName, value) {
|
|
49
|
+
const coerced = coerceBool(value);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!needsPropertyBridge)
|
|
52
|
+
return;
|
|
53
|
+
const el = ref.current;
|
|
54
|
+
if (!el)
|
|
55
|
+
return;
|
|
56
|
+
if (Boolean(el[propName]) !== coerced) {
|
|
57
|
+
el[propName] = coerced;
|
|
58
|
+
}
|
|
59
|
+
}, [coerced, propName, ref]);
|
|
60
|
+
return coerced;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=use-boolean-prop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-boolean-prop.js","sourceRoot":"","sources":["../../src/utils/use-boolean-prop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC3E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,KAAK,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAkC,EAClC,QAAgB,EAChB,KAAc;IAEd,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAClC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,mBAAmB;YAAE,OAAO;QACjC,MAAM,EAAE,GAAG,GAAG,CAAC,OAAc,CAAC;QAC9B,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;YACtC,EAAE,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;QACzB,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IAC7B,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,eAAO,MAAM,OAAO,eAAe,CAAA"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// AUTO-GENERATED FILE - DO NOT EDIT
|
|
2
|
+
// Generated from package.json by scripts/generate-version.js
|
|
3
|
+
// Run 'npm run generate:version' to regenerate.
|
|
4
|
+
/** Current version of tyrell-react. Synced with package.json on build. */
|
|
5
|
+
export const VERSION = '1.0.0-RC10';
|
|
6
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,6DAA6D;AAC7D,gDAAgD;AAEhD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,OAAO,GAAG,YAAY,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tyrell-react",
|
|
3
|
+
"version": "1.0.0-RC10",
|
|
4
|
+
"description": "React wrappers for Tyrell Components",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/components/index.js",
|
|
7
|
+
"types": "./dist/components/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist/**/*",
|
|
10
|
+
"src/**/*",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"generate:version": "node scripts/generate-version.js",
|
|
16
|
+
"build": "npm run generate:version && tsc",
|
|
17
|
+
"clean": "rm -rf dist",
|
|
18
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": ">=18.0.0",
|
|
22
|
+
"react-dom": ">=18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/react": "^18.2.66",
|
|
26
|
+
"@types/react-dom": "^18.2.22",
|
|
27
|
+
"typescript": "^5.7.0"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"react",
|
|
31
|
+
"web-components",
|
|
32
|
+
"typescript",
|
|
33
|
+
"ui-components",
|
|
34
|
+
"tyrell",
|
|
35
|
+
"wrapper",
|
|
36
|
+
"clojurescript",
|
|
37
|
+
"reagent",
|
|
38
|
+
"uix"
|
|
39
|
+
],
|
|
40
|
+
"author": "Gersak <dev@gersak.io>",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/gersak/tyrell.git",
|
|
45
|
+
"directory": "packages/react"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
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;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { useBooleanProperty } from '../utils/use-boolean-prop';
|
|
3
|
+
|
|
4
|
+
type BuiltinFlavor = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'neutral';
|
|
5
|
+
type ShadedFlavor = BuiltinFlavor | `${BuiltinFlavor}+` | `${BuiltinFlavor}-`;
|
|
6
|
+
type ButtonAppearance = 'solid' | 'outlined' | 'ghost';
|
|
7
|
+
|
|
8
|
+
export interface TyButtonCSSProperties extends React.CSSProperties {
|
|
9
|
+
'--ty-button-bg'?: string;
|
|
10
|
+
'--ty-button-bg-hover'?: string;
|
|
11
|
+
'--ty-button-color'?: string;
|
|
12
|
+
'--ty-button-border'?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TyButtonProps extends Omit<React.HTMLAttributes<HTMLElement>, 'style'> {
|
|
16
|
+
style?: TyButtonCSSProperties;
|
|
17
|
+
/**
|
|
18
|
+
* Semantic styling variant. Built-in flavors get themed styles; append `+`
|
|
19
|
+
* for a stronger shade or `-` for a softer one (e.g. `"primary+"`,
|
|
20
|
+
* `"danger-"`). Any other string is passed through as-is — theme it via
|
|
21
|
+
* `--ty-button-*` CSS variables.
|
|
22
|
+
*/
|
|
23
|
+
flavor?: ShadedFlavor | (string & {});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Visual appearance:
|
|
27
|
+
* - `"solid"` (default) — saturated brand fill with paired text color
|
|
28
|
+
* - `"outlined"` — transparent background, text === border
|
|
29
|
+
* - `"ghost"` — text only with hover background
|
|
30
|
+
*/
|
|
31
|
+
appearance?: ButtonAppearance;
|
|
32
|
+
|
|
33
|
+
/** Button size */
|
|
34
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
35
|
+
|
|
36
|
+
/** Button type for form submission */
|
|
37
|
+
type?: 'button' | 'submit' | 'reset';
|
|
38
|
+
|
|
39
|
+
/** Disable the button */
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
|
|
42
|
+
/** Loading state — shows a centered spinner, blocks click, preserves width */
|
|
43
|
+
loading?: boolean;
|
|
44
|
+
|
|
45
|
+
/** Pill-shaped button (rounded ends) */
|
|
46
|
+
pill?: boolean;
|
|
47
|
+
|
|
48
|
+
/** Action (icon-only square) */
|
|
49
|
+
action?: boolean;
|
|
50
|
+
|
|
51
|
+
/** Accessible label for screen readers */
|
|
52
|
+
label?: string;
|
|
53
|
+
|
|
54
|
+
/** Form field name for form submission */
|
|
55
|
+
name?: string;
|
|
56
|
+
|
|
57
|
+
/** Form field value for form submission */
|
|
58
|
+
value?: string;
|
|
59
|
+
|
|
60
|
+
/** Full-width button */
|
|
61
|
+
wide?: boolean;
|
|
62
|
+
|
|
63
|
+
/** Button content */
|
|
64
|
+
children?: React.ReactNode;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const TyButton = React.forwardRef<HTMLElement, TyButtonProps>(
|
|
68
|
+
({
|
|
69
|
+
children,
|
|
70
|
+
type,
|
|
71
|
+
appearance,
|
|
72
|
+
disabled,
|
|
73
|
+
loading,
|
|
74
|
+
pill,
|
|
75
|
+
action,
|
|
76
|
+
wide,
|
|
77
|
+
label,
|
|
78
|
+
name,
|
|
79
|
+
value,
|
|
80
|
+
onClick,
|
|
81
|
+
...props
|
|
82
|
+
}, ref) => {
|
|
83
|
+
const elementRef = useRef<HTMLElement>(null);
|
|
84
|
+
|
|
85
|
+
// Imperatively attach the click listener so onClick reliably fires for the
|
|
86
|
+
// CustomEvent('click') that <ty-button> re-dispatches on its host (the
|
|
87
|
+
// inner <button> calls stopPropagation, so React's delegated onClick can
|
|
88
|
+
// miss it). Also handles type=submit by dispatching a synthetic submit.
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
const element = elementRef.current;
|
|
91
|
+
if (!element) return;
|
|
92
|
+
|
|
93
|
+
const handler = (event: Event) => {
|
|
94
|
+
if (type === 'submit') {
|
|
95
|
+
const form = element.closest('form');
|
|
96
|
+
if (form) {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
event.stopPropagation();
|
|
99
|
+
form.dispatchEvent(new Event('submit', {
|
|
100
|
+
bubbles: true,
|
|
101
|
+
cancelable: true,
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (onClick) {
|
|
106
|
+
onClick(event as unknown as React.MouseEvent<HTMLElement>);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
element.addEventListener('click', handler);
|
|
111
|
+
return () => {
|
|
112
|
+
element.removeEventListener('click', handler);
|
|
113
|
+
};
|
|
114
|
+
}, [type, onClick]);
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (ref && elementRef.current) {
|
|
118
|
+
if (typeof ref === 'function') {
|
|
119
|
+
ref(elementRef.current);
|
|
120
|
+
} else {
|
|
121
|
+
ref.current = elementRef.current;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}, [ref]);
|
|
125
|
+
|
|
126
|
+
const isDisabled = useBooleanProperty(elementRef, 'disabled', disabled);
|
|
127
|
+
const isLoading = useBooleanProperty(elementRef, 'loading', loading);
|
|
128
|
+
const isPill = useBooleanProperty(elementRef, 'pill', pill);
|
|
129
|
+
const isAction = useBooleanProperty(elementRef, 'action', action);
|
|
130
|
+
const isWide = useBooleanProperty(elementRef, 'wide', wide);
|
|
131
|
+
|
|
132
|
+
const webComponentProps: Record<string, any> = {
|
|
133
|
+
...props,
|
|
134
|
+
ref: elementRef,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
if (isDisabled) webComponentProps.disabled = '';
|
|
138
|
+
if (isLoading) webComponentProps.loading = '';
|
|
139
|
+
if (isPill) webComponentProps.pill = '';
|
|
140
|
+
if (isAction) webComponentProps.action = '';
|
|
141
|
+
if (isWide) webComponentProps.wide = '';
|
|
142
|
+
|
|
143
|
+
if (appearance) webComponentProps.appearance = appearance;
|
|
144
|
+
if (type) webComponentProps.type = type;
|
|
145
|
+
if (label) webComponentProps.label = label;
|
|
146
|
+
if (name) webComponentProps.name = name;
|
|
147
|
+
if (value) webComponentProps.value = value;
|
|
148
|
+
|
|
149
|
+
return React.createElement(
|
|
150
|
+
'ty-button',
|
|
151
|
+
webComponentProps,
|
|
152
|
+
children
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
TyButton.displayName = 'TyButton';
|