Skip to content

Latest commit

Β 

History

History
353 lines (272 loc) Β· 8.19 KB

File metadata and controls

353 lines (272 loc) Β· 8.19 KB

@gersak/ty-react

React wrappers for Ty Web Components - bringing framework-agnostic web components to React with full TypeScript support.

🎯 Philosophy

Ty web components are distributed via CDN and loaded as standard web components. These React wrappers provide:

  • βœ… React-friendly API - Props instead of attributes
  • βœ… TypeScript types - Full type safety
  • βœ… Event handling - React synthetic events
  • βœ… Ref forwarding - Direct DOM access
  • βœ… Zero bundle overhead - Just thin wrappers (~2KB)

πŸ“¦ Installation

Step 1: Load Ty Web Components (CDN)

Add to your index.html:

<!DOCTYPE html>
<html>
<head>
  <!-- Ty Web Components & Styles via CDN -->
  <script src="https://cdn.jsdelivr.net/npm/@gersak/ty@latest/dist/ty.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gersak/ty@latest/css/ty.css">
</head>
<body>
  <div id="root"></div>
</body>
</html>

Step 2: Install React Wrappers

npm install @gersak/ty-react

That's it! No build complexity, no bundler configuration.

πŸš€ Quick Start

import React, { useState } from 'react';
import { TyButton, TyInput, TyModal } from '@gersak/ty-react';

function App() {
  const [value, setValue] = useState('');

  return (
    <div>
      <TyButton flavor="primary" onClick={() => alert('Hello!')}>
        Click Me
      </TyButton>
      
      <TyInput 
        placeholder="Enter text..."
        value={value}
        onChange={(e) => setValue(e.detail.value)}  // Fires on every keystroke
      />
    </div>
  );
}

πŸ“š Available Components

All 18 components wrapped:

Component Description
TyButton Semantic button with multiple flavors
TyInput Form input with validation
TyTextarea Multi-line text input
TyCheckbox Checkbox with custom styling
TyDropdown Dropdown selection
TyOption Dropdown option
TyMultiselect Multi-value selection
TyTag Tag/badge component
TyModal Modal dialogs
TyPopup Smart popup positioning
TyTooltip Tooltip component
TyIcon Icon component (3000+ icons)
TyCalendar Full calendar
TyCalendarMonth Month view
TyCalendarNavigation Calendar navigation
TyDatePicker Date picker with calendar
TyTabs Tab container
TyTab Individual tab

⚑ Event Handling (React Convention)

Ty React wrappers follow React conventions for event handling:

Input Components (Input, Textarea, Checkbox)

import { TyInput } from '@gersak/ty-react';

function SearchBox() {
  const [query, setQuery] = useState('');
  
  return (
    <TyInput
      value={query}
      // βœ… onChange fires on every keystroke (React convention)
      onChange={(e) => setQuery(e.detail.value)}
      
      // βœ… onChangeCommit fires on blur (optional - for validation)
      onChangeCommit={(e) => console.log('Final value:', e.detail.value)}
    />
  );
}

Key Points:

  • onChange β†’ Fires on every keystroke (matches React's <input onChange>)
  • onChangeCommit β†’ Fires on blur if value changed (optional)
  • This differs from native DOM where onchange fires on blur

Selection Components (Dropdown, Calendar, etc.)

import { TyDropdown, TyOption } from '@gersak/ty-react';

function CountrySelector() {
  return (
    <TyDropdown 
      // onChange fires when selection changes
      onChange={(e) => console.log('Selected:', e.detail.value)}
    >
      <TyOption value="us">United States</TyOption>
      <TyOption value="ca">Canada</TyOption>
    </TyDropdown>
  );
}

For selection components, onChange fires when the selection changes (as expected).

πŸ’‘ Usage Examples

Form with Validation

import { TyInput, TyDropdown, TyOption, TyButton } from '@gersak/ty-react';

function ContactForm() {
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget as HTMLFormElement);
    console.log(Object.fromEntries(formData));
  };

  return (
    <form onSubmit={handleSubmit}>
      <TyInput 
        name="email"
        type="email" 
        required 
        placeholder="your@email.com"
      />
      
      <TyDropdown name="role" required>
        <TyOption value="admin">Administrator</TyOption>
        <TyOption value="user">User</TyOption>
      </TyDropdown>
      
      <TyButton type="submit" flavor="primary">
        Submit
      </TyButton>
    </form>
  );
}

Modal with Imperative API

import { useRef } from 'react';
import { TyModal, TyButton, type TyModalRef } from '@gersak/ty-react';

function ModalExample() {
  const modalRef = useRef<TyModalRef>(null);

  return (
    <>
      <TyButton onClick={() => modalRef.current?.show()}>
        Open Modal
      </TyButton>
      
      <TyModal 
        ref={modalRef}
        onClose={(e) => console.log('Closed:', e.detail.reason)}
      >
        <h2>Modal Content</h2>
        <TyButton onClick={() => modalRef.current?.hide()}>
          Close
        </TyButton>
      </TyModal>
    </>
  );
}

Calendar with Custom Rendering

import { useState } from 'react';
import { TyCalendar } from '@gersak/ty-react';

function CalendarExample() {
  const [selected, setSelected] = useState<Date | null>(null);

  return (
    <TyCalendar
      value={selected?.getTime()}
      onChange={(e) => setSelected(new Date(e.detail.date))}
      min="2024-01-01"
      max="2024-12-31"
    />
  );
}

🎨 Icons (3000+ Available)

Register icons via CDN before use:

<!-- In index.html -->
<script type="module">
  import { check, heart, star } from 'https://cdn.jsdelivr.net/npm/@gersak/ty@latest/dist/icons/lucide.js';
  
  window.ty.icons.register({ check, heart, star });
</script>

Then use in React:

import { TyIcon, TyButton } from '@gersak/ty-react';

function IconExample() {
  return (
    <>
      <TyIcon name="check" size="lg" />
      
      <TyButton flavor="primary">
        <TyIcon name="heart" />
        Like
      </TyButton>
    </>
  );
}

πŸ“˜ TypeScript Support

Full TypeScript definitions included:

import type { 
  TyButtonProps,
  TyInputEventDetail,
  TyModalRef,
  TyDropdownEventDetail 
} from '@gersak/ty-react';

// Type-safe props
const buttonProps: TyButtonProps = {
  flavor: 'primary',  // βœ… Autocomplete
  size: 'lg',         // βœ… Type-checked
  disabled: false
};

// Type-safe event handlers
const handleChange = (e: CustomEvent<TyInputEventDetail>) => {
  console.log(e.detail.value);      // βœ… Typed
  console.log(e.detail.rawValue);   // βœ… Typed
};

🌐 ClojureScript Support

Works perfectly with Reagent and UIx:

Reagent

(ns my-app.core
  (:require ["@gersak/ty-react" :as ty]))

(defn my-component []
  [:div
   [:> ty/TyButton {:flavor "primary"} "Click Me"]
   [:> ty/TyInput {:placeholder "Enter text..."}]])

UIx

(ns my-app.core
  (:require [uix.core :as uix]
            ["@gersak/ty-react" :refer [TyButton TyInput]]))

(defn my-component []
  (uix/view
    [:<>
     [TyButton {:flavor "primary"} "Click Me"]
     [TyInput {:placeholder "Enter text..."}]]))

πŸ”§ Peer Dependencies

Only React is required:

  • react >=18.0.0
  • react-dom >=18.0.0

Note: The core @gersak/ty web components are loaded via CDN, not as an npm dependency!

🌍 Browser Support

Modern browsers with Web Components support:

  • Chrome 67+
  • Firefox 63+
  • Safari 13.1+
  • Edge 79+

πŸ“ Why CDN Distribution?

Ty web components are framework-agnostic and designed for CDN distribution:

βœ… Zero version conflicts - One version serves all frameworks
βœ… Smaller bundles - Web components load once, shared across all React components
βœ… No build complexity - Just script tags
βœ… Better caching - CDN edge network
βœ… Framework freedom - Same components work in React, Vue, Svelte, vanilla JS

🀝 Contributing

This package is part of the Ty monorepo.

πŸ“„ License

MIT License - see LICENSE

πŸ”— Links