diff --git a/docs/components/autocomplete.md b/docs/components/autocomplete.md new file mode 100644 index 0000000..8456bb6 --- /dev/null +++ b/docs/components/autocomplete.md @@ -0,0 +1,376 @@ +# Autocomplete + +Searchable dropdown with autocomplete functionality, keyboard navigation, and reactive options. + +## Tag + +`Autocomplete` + +## Props + +| Prop | Type | Default | Description | +| :----------- | :------------------------------------------------ | :------------------ | :----------------------------------------------- | +| `label` | `string` | `-` | Label text for the input | +| `options` | `Array` | `[]` | Options to search from | +| `value` | `string \| Signal` | `''` | Selected value | +| `placeholder`| `string` | `'Search...'` | Placeholder text | +| `onSelect` | `function` | `-` | Called when an option is selected | +| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | + +## Live Examples + +### Basic Autocomplete + +
+
+

Live Demo

+
+
+
+ +```javascript +const BasicDemo = () => { + const selected = $(''); + const fruits = ['Apple', 'Banana', 'Orange', 'Grape', 'Strawberry', 'Mango', 'Pineapple', 'Watermelon']; + + return Autocomplete({ + label: 'Search fruit', + options: fruits, + value: selected, + onSelect: (value) => selected(value) + }); +}; +$mount(BasicDemo, '#demo-basic'); +``` + +### With Objects + +
+
+

Live Demo

+
+
+
+ +```javascript +const ObjectsDemo = () => { + const selected = $(''); + const selectedLabel = $(''); + + const countries = [ + { value: 'mx', label: 'Mexico' }, + { value: 'us', label: 'United States' }, + { value: 'ca', label: 'Canada' }, + { value: 'br', label: 'Brazil' }, + { value: 'ar', label: 'Argentina' }, + { value: 'es', label: 'Spain' } + ]; + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Autocomplete({ + label: 'Search country', + options: countries, + value: selectedLabel, + onSelect: (item) => { + const selectedItem = typeof item === 'string' + ? countries.find(c => c.label === item) + : item; + selected(selectedItem?.value || ''); + selectedLabel(selectedItem?.label || ''); + } + }), + Div({ class: 'alert alert-success' }, [ + `Selected: ${selected()} - ${selectedLabel()}` + ]) + ]); +}; +$mount(ObjectsDemo, '#demo-objects'); +``` + +### With Reactive Display + +
+
+

Live Demo

+
+
+
+ +```javascript +const ReactiveDemo = () => { + const selected = $(''); + + const programmingLanguages = [ + 'JavaScript', 'Python', 'Java', 'C++', 'Ruby', 'Go', 'Rust', 'TypeScript', 'Swift', 'Kotlin' + ]; + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Autocomplete({ + label: 'Programming language', + options: programmingLanguages, + value: selected, + onSelect: (value) => selected(value) + }), + () => selected() ? Div({ class: 'alert alert-info' }, [ + `You selected: ${selected()}` + ]) : null + ]); +}; +$mount(ReactiveDemo, '#demo-reactive'); +``` + +### Dynamic Options + +
+
+

Live Demo

+
+
+
+ +```javascript +const DynamicDemo = () => { + const selected = $(''); + const filterType = $('all'); + + const allItems = { + fruits: ['Apple', 'Banana', 'Orange', 'Mango'], + vegetables: ['Carrot', 'Broccoli', 'Spinach', 'Potato'], + all: ['Apple', 'Banana', 'Orange', 'Mango', 'Carrot', 'Broccoli', 'Spinach', 'Potato'] + }; + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Select({ + label: 'Category', + options: [ + { value: 'all', label: 'All items' }, + { value: 'fruits', label: 'Fruits' }, + { value: 'vegetables', label: 'Vegetables' } + ], + value: filterType, + onchange: (e) => filterType(e.target.value) + }), + Autocomplete({ + label: 'Search item', + options: () => allItems[filterType()], + value: selected, + onSelect: (value) => selected(value) + }) + ]); +}; +$mount(DynamicDemo, '#demo-dynamic'); +``` + +### All Variants + +
+
+

Live Demo

+
+
+
+ +```javascript +const VariantsDemo = () => { + const colors = ['Red', 'Blue', 'Green', 'Yellow', 'Purple', 'Orange', 'Pink', 'Brown', 'Black', 'White']; + + return Div({ class: 'flex flex-col gap-4' }, [ + Div({}, [ + Autocomplete({ + label: 'Primary style', + class: 'input-primary', + options: colors, + value: $(''), + placeholder: 'Search colors...' + }) + ]), + Div({}, [ + Autocomplete({ + label: 'Secondary style', + class: 'input-secondary', + options: colors, + value: $(''), + placeholder: 'Search colors...' + }) + ]), + Div({}, [ + Autocomplete({ + label: 'Ghost style', + class: 'input-ghost', + options: colors, + value: $(''), + placeholder: 'Search colors...' + }) + ]) + ]); +}; +$mount(VariantsDemo, '#demo-variants'); +``` + + \ No newline at end of file diff --git a/docs/components/checkbox.md b/docs/components/checkbox.md new file mode 100644 index 0000000..57afef6 --- /dev/null +++ b/docs/components/checkbox.md @@ -0,0 +1,479 @@ +# Checkbox + +Toggle checkbox component with label, tooltip support, and reactive state management. + +## Tag + +`Checkbox` + +## Props + +| Prop | Type | Default | Description | +| :----------- | :--------------------------- | :---------- | :----------------------------------------------- | +| `label` | `string` | `-` | Label text for the checkbox | +| `value` | `boolean \| Signal` | `false` | Checked state | +| `tooltip` | `string` | `-` | Tooltip text on hover | +| `toggle` | `boolean` | `false` | Display as toggle switch instead of checkbox | +| `disabled` | `boolean \| Signal` | `false` | Disabled state | +| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | +| `onclick` | `function` | `-` | Click event handler | + +## Live Examples + +### Basic Checkbox + +
+
+

Live Demo

+
+
+
+ +```javascript +const BasicDemo = () => { + const accepted = $(false); + + return Checkbox({ + label: 'I accept the terms and conditions', + value: accepted, + onclick: () => accepted(!accepted()) + }); +}; +$mount(BasicDemo, '#demo-basic'); +``` + +### Toggle Switch + +
+
+

Live Demo

+
+
+
+ +```javascript +const ToggleDemo = () => { + const enabled = $(false); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Checkbox({ + label: 'Enable notifications', + value: enabled, + toggle: true, + onclick: () => enabled(!enabled()) + }), + () => enabled() + ? Div({ class: 'alert alert-success' }, 'Notifications are ON') + : Div({ class: 'alert alert-soft' }, 'Notifications are OFF') + ]); +}; +$mount(ToggleDemo, '#demo-toggle'); +``` + +### With Tooltip + +
+
+

Live Demo

+
+
+
+ +```javascript +const TooltipDemo = () => { + const darkMode = $(false); + + return Checkbox({ + label: 'Dark mode', + value: darkMode, + tooltip: 'Enable dark theme preference', + onclick: () => darkMode(!darkMode()) + }); +}; +$mount(TooltipDemo, '#demo-tooltip'); +``` + +### Disabled State + +
+
+

Live Demo

+
+
+
+ +```javascript +const DisabledDemo = () => { + return Div({ class: 'flex flex-col gap-3' }, [ + Checkbox({ + label: 'Checked and disabled', + value: true, + disabled: true + }), + Checkbox({ + label: 'Unchecked and disabled', + value: false, + disabled: true + }) + ]); +}; +$mount(DisabledDemo, '#demo-disabled'); +``` + +### Reactive Multiple Selection + +
+
+

Live Demo

+
+
+
+ +```javascript +const MultipleDemo = () => { + const options = [ + { id: 1, label: 'Option 1', selected: $(false) }, + { id: 2, label: 'Option 2', selected: $(false) }, + { id: 3, label: 'Option 3', selected: $(false) } + ]; + + const selectAll = $(false); + + const toggleAll = (value) => { + selectAll(value); + options.forEach(opt => opt.selected(value)); + }; + + const updateSelectAll = () => { + const allSelected = options.every(opt => opt.selected()); + selectAll(allSelected); + }; + + return Div({ class: 'flex flex-col gap-3' }, [ + Checkbox({ + label: 'Select all', + value: selectAll, + onclick: () => toggleAll(!selectAll()) + }), + Div({ class: 'divider my-1' }), + ...options.map(opt => Checkbox({ + label: opt.label, + value: opt.selected, + onclick: () => { + opt.selected(!opt.selected()); + updateSelectAll(); + } + })), + Div({ class: 'mt-2 text-sm opacity-70' }, () => { + const count = options.filter(opt => opt.selected()).length; + return `${count} of ${options.length} selected`; + }) + ]); +}; +$mount(MultipleDemo, '#demo-multiple'); +``` + +### All Variants + +
+
+

Live Demo

+
+
+
+ +```javascript +const VariantsDemo = () => { + const variant1 = $(true); + const variant2 = $(false); + const variant3 = $(true); + + return Div({ class: 'flex flex-col gap-3' }, [ + Div({ class: 'flex items-center gap-4' }, [ + Checkbox({ + label: 'Primary', + value: variant1, + class: 'checkbox-primary', + onclick: () => variant1(!variant1()) + }), + Checkbox({ + label: 'Secondary', + value: variant2, + class: 'checkbox-secondary', + onclick: () => variant2(!variant2()) + }) + ]), + Div({ class: 'flex items-center gap-4' }, [ + Checkbox({ + label: 'Accent', + value: variant3, + class: 'checkbox-accent', + onclick: () => variant3(!variant3()) + }), + Checkbox({ + label: 'Toggle switch', + value: $(false), + toggle: true, + class: 'toggle-primary' + }) + ]) + ]); +}; +$mount(VariantsDemo, '#demo-variants'); +``` + +### Form Example + +
+
+

Live Demo

+
+
+
+ +```javascript +const FormDemo = () => { + const subscribe = $(false); + const weekly = $(false); + const monthly = $(true); + + return Div({ class: 'flex flex-col gap-4' }, [ + Div({ class: 'text-sm font-bold' }, 'Newsletter preferences'), + Checkbox({ + label: 'Subscribe to newsletter', + value: subscribe, + onclick: () => subscribe(!subscribe()) + }), + () => subscribe() ? Div({ class: 'ml-6 flex flex-col gap-2' }, [ + Checkbox({ + label: 'Weekly updates', + value: weekly, + onclick: () => weekly(!weekly()) + }), + Checkbox({ + label: 'Monthly digest', + value: monthly, + onclick: () => monthly(!monthly()) + }) + ]) : null, + () => subscribe() && (weekly() || monthly()) + ? Div({ class: 'alert alert-success text-sm mt-2' }, 'You will receive updates!') + : subscribe() + ? Div({ class: 'alert alert-warning text-sm mt-2' }, 'Select at least one frequency') + : null + ]); +}; +$mount(FormDemo, '#demo-form'); +``` + + diff --git a/docs/components/colorpicker.md b/docs/components/colorpicker.md new file mode 100644 index 0000000..1cddb83 --- /dev/null +++ b/docs/components/colorpicker.md @@ -0,0 +1,382 @@ +# Colorpicker + +Color picker component with preset color palette, reactive value binding, and customizable appearance. + +## Tag + +`Colorpicker` + +## Props + +| Prop | Type | Default | Description | +| :----------- | :--------------------------- | :---------- | :----------------------------------------------- | +| `label` | `string` | `-` | Label text for the button | +| `value` | `string \| Signal` | `'#000000'` | Selected color value (hex format) | +| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | + +## Live Examples + +### Basic Colorpicker + +
+
+

Live Demo

+
+
+
+ +```javascript +const BasicDemo = () => { + const color = $('#3b82f6'); + + return Colorpicker({ + label: 'Pick a color', + value: color + }); +}; +$mount(BasicDemo, '#demo-basic'); +``` + +### With Reactive Preview + +
+
+

Live Demo

+
+
+
+ +```javascript +const PreviewDemo = () => { + const color = $('#10b981'); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Colorpicker({ + label: 'Choose color', + value: color + }), + Div({ + class: 'w-full h-20 rounded-lg shadow-inner transition-all duration-200', + style: () => `background-color: ${color()}` + }, [ + Div({ class: 'text-center leading-20 pt-6 opacity-70' }, () => color()) + ]) + ]); +}; +$mount(PreviewDemo, '#demo-preview'); +``` + +### Color Palette Grid + +
+
+

Live Demo

+
+
+
+ +```javascript +const PaletteDemo = () => { + const selectedColor = $('#ef4444'); + const presets = [ + '#ef4444', '#f97316', '#f59e0b', '#eab308', + '#84cc16', '#10b981', '#14b8a6', '#06b6d4', + '#3b82f6', '#6366f1', '#8b5cf6', '#d946ef', + '#ec489a', '#f43f5e', '#6b7280', '#1f2937' + ]; + + return Div({ class: 'flex flex-col gap-4' }, [ + Colorpicker({ + label: 'Custom color', + value: selectedColor + }), + Div({ class: 'divider text-xs' }, 'Or choose from palette'), + Div({ class: 'grid grid-cols-8 gap-2' }, presets.map(color => + Button({ + class: `w-8 h-8 rounded-lg shadow-sm transition-transform hover:scale-110`, + style: `background-color: ${color}`, + onclick: () => selectedColor(color) + }) + )), + Div({ class: 'mt-2 text-center text-sm font-mono' }, () => selectedColor()) + ]); +}; +$mount(PaletteDemo, '#demo-palette'); +``` + +### With Text Color Preview + +
+
+

Live Demo

+
+
+
+ +```javascript +const TextDemo = () => { + const bgColor = $('#1e293b'); + const textColor = $('#f8fafc'); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Div({ class: 'flex gap-4' }, [ + Colorpicker({ + label: 'Background', + value: bgColor + }), + Colorpicker({ + label: 'Text', + value: textColor + }) + ]), + Div({ + class: 'p-6 rounded-lg text-center font-bold transition-all duration-200', + style: () => `background-color: ${bgColor()}; color: ${textColor()}` + }, [ + 'Preview text with your colors' + ]) + ]); +}; +$mount(TextDemo, '#demo-text'); +``` + +### All Variants + +
+
+

Live Demo

+
+
+
+ +```javascript +const VariantsDemo = () => { + return Div({ class: 'flex flex-wrap gap-4 items-center' }, [ + Colorpicker({ + label: 'Primary', + value: $('#3b82f6') + }), + Colorpicker({ + label: 'Success', + value: $('#10b981') + }), + Colorpicker({ + label: 'Warning', + value: $('#f59e0b') + }), + Colorpicker({ + label: 'Error', + value: $('#ef4444') + }) + ]); +}; +$mount(VariantsDemo, '#demo-variants'); +``` + +### Dynamic Color Swatch + +
+
+

Live Demo

+
+
+
+ +```javascript +const DynamicDemo = () => { + const primary = $('#3b82f6'); + const secondary = $('#ef4444'); + const accent = $('#10b981'); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Div({ class: 'flex flex-wrap gap-4' }, [ + Colorpicker({ label: 'Primary', value: primary }), + Colorpicker({ label: 'Secondary', value: secondary }), + Colorpicker({ label: 'Accent', value: accent }) + ]), + Div({ class: 'grid grid-cols-3 gap-2 mt-2' }, [ + Div({ + class: 'h-12 rounded-lg shadow-sm flex items-center justify-center text-xs font-bold', + style: () => `background-color: ${primary()}; color: white` + }, 'Primary'), + Div({ + class: 'h-12 rounded-lg shadow-sm flex items-center justify-center text-xs font-bold', + style: () => `background-color: ${secondary()}; color: white` + }, 'Secondary'), + Div({ + class: 'h-12 rounded-lg shadow-sm flex items-center justify-center text-xs font-bold', + style: () => `background-color: ${accent()}; color: white` + }, 'Accent') + ]) + ]); +}; +$mount(DynamicDemo, '#demo-dynamic'); +``` + + diff --git a/docs/components/datepicker.md b/docs/components/datepicker.md new file mode 100644 index 0000000..272444a --- /dev/null +++ b/docs/components/datepicker.md @@ -0,0 +1,339 @@ +# Datepicker + +Date and date range picker component with calendar interface, time selection, and reactive state management. + +## Tag + +`Datepicker` + +## Props + +| Prop | Type | Default | Description | +| :----------- | :------------------------------------------------ | :----------------------- | :----------------------------------------------- | +| `label` | `string` | `-` | Label text for the input | +| `value` | `string \| {start: string, end: string} \| Signal` | `-` | Selected date or range | +| `range` | `boolean` | `false` | Enable date range selection mode | +| `hour` | `boolean` | `false` | Enable hour selection | +| `placeholder`| `string` | `'Select date...'` | Placeholder text | +| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | + +## Live Examples + +### Basic Datepicker + +
+
+

Live Demo

+
+
+
+ +```javascript +const BasicDemo = () => { + const date = $(''); + + return Datepicker({ + label: 'Select date', + value: date, + placeholder: 'Choose a date...' + }); +}; +$mount(BasicDemo, '#demo-basic'); +``` + +### Date Range Picker + +
+
+

Live Demo

+
+
+
+ +```javascript +const RangeDemo = () => { + const range = $({ start: '', end: '' }); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Datepicker({ + label: 'Date range', + value: range, + range: true, + placeholder: 'Select start and end date...' + }), + () => range().start && range().end ? Div({ class: 'alert alert-success' }, [ + `Selected: ${range().start} → ${range().end}` + ]) : null + ]); +}; +$mount(RangeDemo, '#demo-range'); +``` + +### With Time Selection + +
+
+

Live Demo

+
+
+
+ +```javascript +const TimeDemo = () => { + const datetime = $(''); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Datepicker({ + label: 'Select date and time', + value: datetime, + hour: true, + placeholder: 'Choose date and time...' + }), + () => datetime() ? Div({ class: 'alert alert-info' }, [ + `Selected: ${datetime()}` + ]) : null + ]); +}; +$mount(TimeDemo, '#demo-time'); +``` + +### Range with Time + +
+
+

Live Demo

+
+
+
+ +```javascript +const RangeTimeDemo = () => { + const range = $({ start: '', end: '', startHour: 9, endHour: 17 }); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Datepicker({ + label: 'Schedule range', + value: range, + range: true, + hour: true, + placeholder: 'Select date and time range...' + }), + () => range().start && range().end ? Div({ class: 'alert alert-primary' }, [ + `From ${range().start} ${range().startHour || 9}:00 to ${range().end} ${range().endHour || 17}:00` + ]) : null + ]); +}; +$mount(RangeTimeDemo, '#demo-range-time'); +``` + +### Reactive Display + +
+
+

Live Demo

+
+
+
+ +```javascript +const ReactiveDemo = () => { + const date = $(''); + const today = new Date().toISOString().split('T')[0]; + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Datepicker({ + label: 'Select date', + value: date, + placeholder: 'Choose a date...' + }), + Div({ class: 'stats shadow' }, [ + Div({ class: 'stat' }, [ + Div({ class: 'stat-title' }, 'Selected date'), + Div({ class: 'stat-value text-sm' }, () => date() || 'Not selected'), + Div({ class: 'stat-desc' }, () => date() === today ? 'Today' : '') + ]) + ]) + ]); +}; +$mount(ReactiveDemo, '#demo-reactive'); +``` + +### All Variants + +
+
+

Live Demo

+
+
+
+ +```javascript +const VariantsDemo = () => { + return Div({ class: 'flex flex-col gap-4' }, [ + Datepicker({ + label: 'Single date', + value: $('2024-12-25'), + placeholder: 'Select date...' + }), + Datepicker({ + label: 'Date range', + range: true, + value: $({ start: '2024-12-01', end: '2024-12-31' }), + placeholder: 'Select range...' + }), + Datepicker({ + label: 'With time', + hour: true, + value: $('2024-12-25T14:00:00'), + placeholder: 'Select date and time...' + }) + ]); +}; +$mount(VariantsDemo, '#demo-variants'); +``` + + diff --git a/docs/components/input.md b/docs/components/input.md new file mode 100644 index 0000000..9962c6c --- /dev/null +++ b/docs/components/input.md @@ -0,0 +1,348 @@ +# Input + +Form input component with floating label, icons, password toggle, tooltip, and error states. Fully integrated with DaisyUI and Tailwind. + +## Tag + +`Input` + +## Props + +| Prop | Type | Default | Description | +| :----------- | :--------------------------- | :--------- | :----------------------------------------------- | +| `label` | `string` | `-` | Label text (floating style) | +| `type` | `string` | `'text'` | Input type (text, password, email, number, date) | +| `value` | `string \| Signal` | `''` | Input value | +| `placeholder`| `string` | `' '` | Placeholder text | +| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed inside input | +| `tip` | `string` | `-` | Help tooltip text | +| `error` | `string \| Signal` | `-` | Error message to display | +| `disabled` | `boolean \| Signal` | `false` | Disabled state | +| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | +| `oninput` | `function` | `-` | Input event handler | + +## Live Examples + +### Basic Input + +
+
+

Live Demo

+
+
+
+ +```javascript +const BasicDemo = () => { + const name = $(''); + + return Input({ + label: 'Full Name', + placeholder: 'Enter your name', + value: name, + oninput: (e) => name(e.target.value) + }); +}; +$mount(BasicDemo, '#demo-basic'); +``` + +### With Icon + +
+
+

Live Demo

+
+
+
+ +```javascript +const IconDemo = () => { + const email = $(''); + + return Input({ + label: 'Email', + type: 'email', + icon: Icons.iconMail, + value: email, + oninput: (e) => email(e.target.value) + }); +}; +$mount(IconDemo, '#demo-icon'); +``` + +### Password with Toggle + +
+
+

Live Demo

+
+
+
+ +```javascript +const PasswordDemo = () => { + const password = $(''); + + return Input({ + label: 'Password', + type: 'password', + value: password, + oninput: (e) => password(e.target.value) + }); +}; +$mount(PasswordDemo, '#demo-password'); +``` + +### With Tooltip + +
+
+

Live Demo

+
+
+
+ +```javascript +const TooltipDemo = () => { + const username = $(''); + + return Input({ + label: 'Username', + tip: 'Must be at least 3 characters', + value: username, + oninput: (e) => username(e.target.value) + }); +}; +$mount(TooltipDemo, '#demo-tooltip'); +``` + +### Error State + +
+
+

Live Demo

+
+
+
+ +```javascript +const ErrorDemo = () => { + const email = $(''); + const isValid = $(true); + + const validate = (value) => { + const valid = value.includes('@') && value.includes('.'); + isValid(valid); + email(value); + }; + + return Input({ + label: 'Email', + type: 'email', + value: email, + error: () => !isValid() && email() ? 'Invalid email address' : '', + oninput: (e) => validate(e.target.value) + }); +}; +$mount(ErrorDemo, '#demo-error'); +``` + +### Disabled State + +
+
+

Live Demo

+
+
+
+ +```javascript +const DisabledDemo = () => { + return Input({ + label: 'Username', + value: 'john.doe', + disabled: true + }); +}; +$mount(DisabledDemo, '#demo-disabled'); +``` + +### All Variants + +
+
+

Live Demo

+
+
+
+ +```javascript +const VariantsDemo = () => { + const text = $(''); + const number = $(0); + + return Div({ class: 'flex flex-col gap-4' }, [ + Input({ + label: 'Text Input', + placeholder: 'Type something...', + value: text, + oninput: (e) => text(e.target.value) + }), + Input({ + label: 'Number Input', + type: 'number', + value: number, + oninput: (e) => number(parseInt(e.target.value) || 0) + }), + Input({ + label: 'Date Input', + type: 'date', + value: $('2024-01-01') + }) + ]); +}; +$mount(VariantsDemo, '#demo-variants'); +``` + + diff --git a/docs/components/radio.md b/docs/components/radio.md new file mode 100644 index 0000000..ea7991f --- /dev/null +++ b/docs/components/radio.md @@ -0,0 +1,697 @@ +# Radio + +Radio button component with label, tooltip support, and reactive group selection. + +## Tag + +`Radio` + +## Props + +| Prop | Type | Default | Description | +| :----------- | :--------------------------- | :---------- | :----------------------------------------------- | +| `label` | `string` | `-` | Label text for the radio button | +| `value` | `string \| Signal` | `-` | Selected value (for group) | +| `radioValue` | `string` | `-` | Value of this radio button | +| `tooltip` | `string` | `-` | Tooltip text on hover | +| `disabled` | `boolean \| Signal` | `false` | Disabled state | +| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | +| `onclick` | `function` | `-` | Click event handler | + +## Live Examples + +### Basic Radio Group + +
+
+

Live Demo

+
+
+
+ +```javascript +const BasicDemo = () => { + const selected = $('option1'); + + return Div({ class: 'flex flex-col gap-3' }, [ + Radio({ + label: 'Option 1', + value: selected, + radioValue: 'option1', + onclick: () => selected('option1') + }), + Radio({ + label: 'Option 2', + value: selected, + radioValue: 'option2', + onclick: () => selected('option2') + }), + Radio({ + label: 'Option 3', + value: selected, + radioValue: 'option3', + onclick: () => selected('option3') + }), + Div({ class: 'mt-2 text-sm opacity-70' }, () => `Selected: ${selected()}`) + ]); +}; +$mount(BasicDemo, '#demo-basic'); +``` + +### With Tooltip + +
+
+

Live Demo

+
+
+
+ +```javascript +const TooltipDemo = () => { + const selected = $('light'); + + return Div({ class: 'flex flex-col gap-3' }, [ + Radio({ + label: 'Light mode', + value: selected, + radioValue: 'light', + tooltip: 'Light theme with white background', + onclick: () => selected('light') + }), + Radio({ + label: 'Dark mode', + value: selected, + radioValue: 'dark', + tooltip: 'Dark theme with black background', + onclick: () => selected('dark') + }) + ]); +}; +$mount(TooltipDemo, '#demo-tooltip'); +``` + +### Disabled State + +
+
+

Live Demo

+
+
+
+ +```javascript +const DisabledDemo = () => { + const selected = $('enabled'); + + return Div({ class: 'flex flex-col gap-3' }, [ + Radio({ + label: 'Enabled option', + value: selected, + radioValue: 'enabled', + onclick: () => selected('enabled') + }), + Radio({ + label: 'Disabled option (cannot select)', + value: selected, + radioValue: 'disabled', + disabled: true, + onclick: () => selected('disabled') + }) + ]); +}; +$mount(DisabledDemo, '#demo-disabled'); +``` + +### Reactive Preview + +
+
+

Live Demo

+
+
+
+ +```javascript +const ReactiveDemo = () => { + const size = $('medium'); + const color = $('blue'); + + const sizes = [ + { value: 'small', label: 'Small' }, + { value: 'medium', label: 'Medium' }, + { value: 'large', label: 'Large' } + ]; + + const colors = [ + { value: 'blue', label: 'Blue', class: 'text-blue-600' }, + { value: 'green', label: 'Green', class: 'text-green-600' }, + { value: 'red', label: 'Red', class: 'text-red-600' } + ]; + + return Div({ class: 'flex flex-col gap-4' }, [ + Div({ class: 'text-sm font-bold' }, 'Select size'), + Div({ class: 'flex gap-4' }, sizes.map(s => + Radio({ + label: s.label, + value: size, + radioValue: s.value, + onclick: () => size(s.value) + }) + )), + Div({ class: 'text-sm font-bold mt-2' }, 'Select color'), + Div({ class: 'flex gap-4' }, colors.map(c => + Radio({ + label: c.label, + value: color, + radioValue: c.value, + onclick: () => color(c.value) + }) + )), + Div({ + class: 'mt-4 p-4 rounded-lg text-center transition-all', + style: () => { + const sizeMap = { small: 'text-sm', medium: 'text-base', large: 'text-lg' }; + const colorMap = { blue: '#3b82f6', green: '#10b981', red: '#ef4444' }; + return `background-color: ${colorMap[color()]}; color: white; ${sizeMap[size()]}`; + } + }, () => `${size()} ${color()} preview`) + ]); +}; +$mount(ReactiveDemo, '#demo-reactive'); +``` + +### Payment Method Selection + +
+
+

Live Demo

+
+
+
+ +```javascript +const PaymentDemo = () => { + const method = $('credit'); + + return Div({ class: 'flex flex-col gap-4' }, [ + Div({ class: 'text-sm font-bold' }, 'Payment method'), + Div({ class: 'flex flex-col gap-3' }, [ + Radio({ + label: '💳 Credit Card', + value: method, + radioValue: 'credit', + onclick: () => method('credit') + }), + Radio({ + label: '🏦 Bank Transfer', + value: method, + radioValue: 'bank', + onclick: () => method('bank') + }), + Radio({ + label: '📱 Digital Wallet', + value: method, + radioValue: 'wallet', + onclick: () => method('wallet') + }) + ]), + Div({ class: 'alert alert-info mt-2' }, () => { + const messages = { + credit: 'You selected Credit Card. Enter your card details.', + bank: 'You selected Bank Transfer. Use the provided account number.', + wallet: 'You selected Digital Wallet. Scan the QR code to pay.' + }; + return messages[method()]; + }) + ]); +}; +$mount(PaymentDemo, '#demo-payment'); +``` + +### All Variants + +
+
+

Live Demo

+
+
+
+ +```javascript +const VariantsDemo = () => { + const primary = $('primary1'); + const secondary = $('secondary1'); + const accent = $('accent1'); + + return Div({ class: 'flex flex-col gap-4' }, [ + Div({ class: 'card bg-base-200 p-4' }, [ + Div({ class: 'text-sm font-bold mb-2' }, 'Primary variant'), + Div({ class: 'flex gap-4' }, [ + Radio({ + label: 'Option A', + value: primary, + radioValue: 'primary1', + class: 'radio-primary', + onclick: () => primary('primary1') + }), + Radio({ + label: 'Option B', + value: primary, + radioValue: 'primary2', + class: 'radio-primary', + onclick: () => primary('primary2') + }) + ]) + ]), + Div({ class: 'card bg-base-200 p-4' }, [ + Div({ class: 'text-sm font-bold mb-2' }, 'Secondary variant'), + Div({ class: 'flex gap-4' }, [ + Radio({ + label: 'Option A', + value: secondary, + radioValue: 'secondary1', + class: 'radio-secondary', + onclick: () => secondary('secondary1') + }), + Radio({ + label: 'Option B', + value: secondary, + radioValue: 'secondary2', + class: 'radio-secondary', + onclick: () => secondary('secondary2') + }) + ]) + ]), + Div({ class: 'card bg-base-200 p-4' }, [ + Div({ class: 'text-sm font-bold mb-2' }, 'Accent variant'), + Div({ class: 'flex gap-4' }, [ + Radio({ + label: 'Option A', + value: accent, + radioValue: 'accent1', + class: 'radio-accent', + onclick: () => accent('accent1') + }), + Radio({ + label: 'Option B', + value: accent, + radioValue: 'accent2', + class: 'radio-accent', + onclick: () => accent('accent2') + }) + ]) + ]) + ]); +}; +$mount(VariantsDemo, '#demo-variants'); +``` + +### Dynamic Options + +
+
+

Live Demo

+
+
+
+ +```javascript +const DynamicDemo = () => { + const category = $('cars'); + const selected = $(''); + + const options = { + cars: [ + { value: 'sedan', label: 'Sedan' }, + { value: 'suv', label: 'SUV' }, + { value: 'sports', label: 'Sports' } + ], + colors: [ + { value: 'red', label: 'Red' }, + { value: 'blue', label: 'Blue' }, + { value: 'black', label: 'Black' } + ] + }; + + return Div({ class: 'flex flex-col gap-4' }, [ + Div({ class: 'flex gap-4' }, [ + Radio({ + label: 'Cars', + value: category, + radioValue: 'cars', + onclick: () => { + category('cars'); + selected(''); + } + }), + Radio({ + label: 'Colors', + value: category, + radioValue: 'colors', + onclick: () => { + category('colors'); + selected(''); + } + }) + ]), + Div({ class: 'divider my-1' }), + Div({ class: 'text-sm font-bold' }, () => `Select ${category()}`), + Div({ class: 'flex flex-col gap-2' }, () => + options[category()].map(opt => + Radio({ + label: opt.label, + value: selected, + radioValue: opt.value, + onclick: () => selected(opt.value) + }) + ) + ), + () => selected() + ? Div({ class: 'alert alert-success mt-2' }, () => `Selected ${category()}: ${selected()}`) + : null + ]); +}; +$mount(DynamicDemo, '#demo-dynamic'); +``` + + diff --git a/docs/components/rating.md b/docs/components/rating.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/components/select.md b/docs/components/select.md new file mode 100644 index 0000000..2d45a4a --- /dev/null +++ b/docs/components/select.md @@ -0,0 +1,366 @@ +# Select + +Dropdown select component with full DaisyUI styling, reactive options, and form integration. + +## Tag + +`Select` + +## Props + +| Prop | Type | Default | Description | +| :----------- | :-------------------------------------- | :------------------ | :----------------------------------------------- | +| `label` | `string` | `-` | Label text above select | +| `options` | `Array<{value: string, label: string}>` | `[]` | Array of options with value and label | +| `value` | `string \| Signal` | `''` | Selected value | +| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) | +| `disabled` | `boolean \| Signal` | `false` | Disabled state | +| `onchange` | `function` | `-` | Change event handler | + +## Live Examples + +### Basic Select + +
+
+

Live Demo

+
+
+
+ +```javascript +const BasicDemo = () => { + const selected = $('apple'); + + return Select({ + label: 'Choose a fruit', + options: [ + { value: 'apple', label: '🍎 Apple' }, + { value: 'banana', label: '🍌 Banana' }, + { value: 'orange', label: '🍊 Orange' }, + { value: 'grape', label: '🍇 Grape' } + ], + value: selected, + onchange: (e) => selected(e.target.value) + }); +}; +$mount(BasicDemo, '#demo-basic'); +``` + +### With Reactive Display + +
+
+

Live Demo

+
+
+
+ +```javascript +const ReactiveDemo = () => { + const selected = $('small'); + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Select({ + label: 'Select size', + options: [ + { value: 'small', label: 'Small' }, + { value: 'medium', label: 'Medium' }, + { value: 'large', label: 'Large' } + ], + value: selected, + onchange: (e) => selected(e.target.value) + }), + Div({ class: 'alert alert-info' }, [ + `You selected: ${selected()}` + ]) + ]); +}; +$mount(ReactiveDemo, '#demo-reactive'); +``` + +### Disabled State + +
+
+

Live Demo

+
+
+
+ +```javascript +const DisabledDemo = () => { + return Select({ + label: 'Country (disabled)', + options: [ + { value: 'mx', label: 'Mexico' }, + { value: 'us', label: 'United States' }, + { value: 'ca', label: 'Canada' } + ], + value: 'mx', + disabled: true + }); +}; +$mount(DisabledDemo, '#demo-disabled'); +``` + +### Dynamic Options + +
+
+

Live Demo

+
+
+
+ +```javascript +const DynamicDemo = () => { + const category = $('fruits'); + + const options = { + fruits: [ + { value: 'apple', label: '🍎 Apple' }, + { value: 'banana', label: '🍌 Banana' } + ], + vegetables: [ + { value: 'carrot', label: '🥕 Carrot' }, + { value: 'broccoli', label: '🥦 Broccoli' } + ] + }; + + return Div({ class: 'flex flex-col gap-4 w-full' }, [ + Select({ + label: 'Category', + options: [ + { value: 'fruits', label: 'Fruits' }, + { value: 'vegetables', label: 'Vegetables' } + ], + value: category, + onchange: (e) => category(e.target.value) + }), + Select({ + label: 'Item', + options: () => options[category()] || [], + value: $(''), + onchange: (e) => console.log('Selected:', e.target.value) + }) + ]); +}; +$mount(DynamicDemo, '#demo-dynamic'); +``` + +### All Variants + +
+
+

Live Demo

+
+
+
+ +```javascript +const VariantsDemo = () => { + const primary = $('option1'); + const secondary = $('option2'); + const accent = $(''); + + return Div({ class: 'flex flex-col gap-4' }, [ + Select({ + label: 'Primary Select', + class: 'select-primary', + options: [ + { value: 'option1', label: 'Option 1' }, + { value: 'option2', label: 'Option 2' }, + { value: 'option3', label: 'Option 3' } + ], + value: primary, + onchange: (e) => primary(e.target.value) + }), + Select({ + label: 'Secondary Select', + class: 'select-secondary', + options: [ + { value: 'option1', label: 'Option 1' }, + { value: 'option2', label: 'Option 2' } + ], + value: secondary, + onchange: (e) => secondary(e.target.value) + }), + Select({ + label: 'Ghost Select', + class: 'select-ghost', + options: [ + { value: '', label: 'Select an option' }, + { value: 'opt1', label: 'Option 1' }, + { value: 'opt2', label: 'Option 2' } + ], + value: accent, + onchange: (e) => accent(e.target.value) + }) + ]); +}; +$mount(VariantsDemo, '#demo-variants'); +``` + + diff --git a/docs/components/swap.md b/docs/components/swap.md new file mode 100644 index 0000000..e69de29