New docs
This commit is contained in:
376
docs/components/autocomplete.md
Normal file
376
docs/components/autocomplete.md
Normal file
@@ -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<string \| {value: string, label: string}>` | `[]` | Options to search from |
|
||||
| `value` | `string \| Signal<string>` | `''` | 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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-objects" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-dynamic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initAutocompleteExamples = () => {
|
||||
|
||||
// 1. Basic Autocomplete
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
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, basicTarget);
|
||||
}
|
||||
|
||||
// 2. With Objects
|
||||
const objectsTarget = document.querySelector('#demo-objects');
|
||||
if (objectsTarget && !objectsTarget.hasChildNodes()) {
|
||||
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, objectsTarget);
|
||||
}
|
||||
|
||||
// 3. Reactive Display
|
||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
||||
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, reactiveTarget);
|
||||
}
|
||||
|
||||
// 4. Dynamic Options
|
||||
const dynamicTarget = document.querySelector('#demo-dynamic');
|
||||
if (dynamicTarget && !dynamicTarget.hasChildNodes()) {
|
||||
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, dynamicTarget);
|
||||
}
|
||||
|
||||
// 5. All Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
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, variantsTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initAutocompleteExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initAutocompleteExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
479
docs/components/checkbox.md
Normal file
479
docs/components/checkbox.md
Normal file
@@ -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<boolean>` | `false` | Checked state |
|
||||
| `tooltip` | `string` | `-` | Tooltip text on hover |
|
||||
| `toggle` | `boolean` | `false` | Display as toggle switch instead of checkbox |
|
||||
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||
| `onclick` | `function` | `-` | Click event handler |
|
||||
|
||||
## Live Examples
|
||||
|
||||
### Basic Checkbox
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-toggle" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-tooltip" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-disabled" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-multiple" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-form" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initCheckboxExamples = () => {
|
||||
|
||||
// 1. Basic Checkbox
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
const BasicDemo = () => {
|
||||
const accepted = $(false);
|
||||
|
||||
return Checkbox({
|
||||
label: 'I accept the terms and conditions',
|
||||
value: accepted,
|
||||
onclick: () => accepted(!accepted())
|
||||
});
|
||||
};
|
||||
$mount(BasicDemo, basicTarget);
|
||||
}
|
||||
|
||||
// 2. Toggle Switch
|
||||
const toggleTarget = document.querySelector('#demo-toggle');
|
||||
if (toggleTarget && !toggleTarget.hasChildNodes()) {
|
||||
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, toggleTarget);
|
||||
}
|
||||
|
||||
// 3. With Tooltip
|
||||
const tooltipTarget = document.querySelector('#demo-tooltip');
|
||||
if (tooltipTarget && !tooltipTarget.hasChildNodes()) {
|
||||
const TooltipDemo = () => {
|
||||
const darkMode = $(false);
|
||||
|
||||
return Checkbox({
|
||||
label: 'Dark mode',
|
||||
value: darkMode,
|
||||
tooltip: 'Enable dark theme preference',
|
||||
onclick: () => darkMode(!darkMode())
|
||||
});
|
||||
};
|
||||
$mount(TooltipDemo, tooltipTarget);
|
||||
}
|
||||
|
||||
// 4. Disabled State
|
||||
const disabledTarget = document.querySelector('#demo-disabled');
|
||||
if (disabledTarget && !disabledTarget.hasChildNodes()) {
|
||||
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, disabledTarget);
|
||||
}
|
||||
|
||||
// 5. Reactive Multiple Selection
|
||||
const multipleTarget = document.querySelector('#demo-multiple');
|
||||
if (multipleTarget && !multipleTarget.hasChildNodes()) {
|
||||
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, multipleTarget);
|
||||
}
|
||||
|
||||
// 6. All Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
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, variantsTarget);
|
||||
}
|
||||
|
||||
// 7. Form Example
|
||||
const formTarget = document.querySelector('#demo-form');
|
||||
if (formTarget && !formTarget.hasChildNodes()) {
|
||||
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, formTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initCheckboxExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initCheckboxExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
382
docs/components/colorpicker.md
Normal file
382
docs/components/colorpicker.md
Normal file
@@ -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<string>` | `'#000000'` | Selected color value (hex format) |
|
||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||
|
||||
## Live Examples
|
||||
|
||||
### Basic Colorpicker
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const BasicDemo = () => {
|
||||
const color = $('#3b82f6');
|
||||
|
||||
return Colorpicker({
|
||||
label: 'Pick a color',
|
||||
value: color
|
||||
});
|
||||
};
|
||||
$mount(BasicDemo, '#demo-basic');
|
||||
```
|
||||
|
||||
### With Reactive Preview
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-preview" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-palette" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-text" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-dynamic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initColorpickerExamples = () => {
|
||||
|
||||
// 1. Basic Colorpicker
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
const BasicDemo = () => {
|
||||
const color = $('#3b82f6');
|
||||
return Colorpicker({
|
||||
label: 'Pick a color',
|
||||
value: color
|
||||
});
|
||||
};
|
||||
$mount(BasicDemo, basicTarget);
|
||||
}
|
||||
|
||||
// 2. With Reactive Preview
|
||||
const previewTarget = document.querySelector('#demo-preview');
|
||||
if (previewTarget && !previewTarget.hasChildNodes()) {
|
||||
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 flex items-center justify-center',
|
||||
style: () => `background-color: ${color()}`
|
||||
}, [
|
||||
Div({ class: 'text-center font-mono text-sm bg-black/20 px-2 py-1 rounded' }, () => color())
|
||||
])
|
||||
]);
|
||||
};
|
||||
$mount(PreviewDemo, previewTarget);
|
||||
}
|
||||
|
||||
// 3. Color Palette Grid
|
||||
const paletteTarget = document.querySelector('#demo-palette');
|
||||
if (paletteTarget && !paletteTarget.hasChildNodes()) {
|
||||
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, paletteTarget);
|
||||
}
|
||||
|
||||
// 4. With Text Color Preview
|
||||
const textTarget = document.querySelector('#demo-text');
|
||||
if (textTarget && !textTarget.hasChildNodes()) {
|
||||
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, textTarget);
|
||||
}
|
||||
|
||||
// 5. All Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
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, variantsTarget);
|
||||
}
|
||||
|
||||
// 6. Dynamic Color Swatch
|
||||
const dynamicTarget = document.querySelector('#demo-dynamic');
|
||||
if (dynamicTarget && !dynamicTarget.hasChildNodes()) {
|
||||
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 text-white',
|
||||
style: () => `background-color: ${primary()}`
|
||||
}, 'Primary'),
|
||||
Div({
|
||||
class: 'h-12 rounded-lg shadow-sm flex items-center justify-center text-xs font-bold text-white',
|
||||
style: () => `background-color: ${secondary()}`
|
||||
}, 'Secondary'),
|
||||
Div({
|
||||
class: 'h-12 rounded-lg shadow-sm flex items-center justify-center text-xs font-bold text-white',
|
||||
style: () => `background-color: ${accent()}`
|
||||
}, 'Accent')
|
||||
])
|
||||
]);
|
||||
};
|
||||
$mount(DynamicDemo, dynamicTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initColorpickerExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initColorpickerExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
339
docs/components/datepicker.md
Normal file
339
docs/components/datepicker.md
Normal file
@@ -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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const BasicDemo = () => {
|
||||
const date = $('');
|
||||
|
||||
return Datepicker({
|
||||
label: 'Select date',
|
||||
value: date,
|
||||
placeholder: 'Choose a date...'
|
||||
});
|
||||
};
|
||||
$mount(BasicDemo, '#demo-basic');
|
||||
```
|
||||
|
||||
### Date Range Picker
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-range" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-time" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-range-time" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initDatepickerExamples = () => {
|
||||
|
||||
// 1. Basic Datepicker
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
const BasicDemo = () => {
|
||||
const date = $('');
|
||||
|
||||
return Datepicker({
|
||||
label: 'Select date',
|
||||
value: date,
|
||||
placeholder: 'Choose a date...'
|
||||
});
|
||||
};
|
||||
$mount(BasicDemo, basicTarget);
|
||||
}
|
||||
|
||||
// 2. Date Range Picker
|
||||
const rangeTarget = document.querySelector('#demo-range');
|
||||
if (rangeTarget && !rangeTarget.hasChildNodes()) {
|
||||
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, rangeTarget);
|
||||
}
|
||||
|
||||
// 3. With Time Selection
|
||||
const timeTarget = document.querySelector('#demo-time');
|
||||
if (timeTarget && !timeTarget.hasChildNodes()) {
|
||||
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, timeTarget);
|
||||
}
|
||||
|
||||
// 4. Range with Time
|
||||
const rangeTimeTarget = document.querySelector('#demo-range-time');
|
||||
if (rangeTimeTarget && !rangeTimeTarget.hasChildNodes()) {
|
||||
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, rangeTimeTarget);
|
||||
}
|
||||
|
||||
// 5. Reactive Display
|
||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
||||
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, reactiveTarget);
|
||||
}
|
||||
|
||||
// 6. All Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
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, variantsTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initDatepickerExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initDatepickerExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
348
docs/components/input.md
Normal file
348
docs/components/input.md
Normal file
@@ -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<string>` | `''` | Input value |
|
||||
| `placeholder`| `string` | `' '` | Placeholder text |
|
||||
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed inside input |
|
||||
| `tip` | `string` | `-` | Help tooltip text |
|
||||
| `error` | `string \| Signal<string>` | `-` | Error message to display |
|
||||
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||
| `oninput` | `function` | `-` | Input event handler |
|
||||
|
||||
## Live Examples
|
||||
|
||||
### Basic Input
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-icon" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-password" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-tooltip" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-error" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-disabled" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const DisabledDemo = () => {
|
||||
return Input({
|
||||
label: 'Username',
|
||||
value: 'john.doe',
|
||||
disabled: true
|
||||
});
|
||||
};
|
||||
$mount(DisabledDemo, '#demo-disabled');
|
||||
```
|
||||
|
||||
### All Variants
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initInputExamples = () => {
|
||||
|
||||
// 1. Basic Input
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
const BasicDemo = () => {
|
||||
const name = $('');
|
||||
return Input({
|
||||
label: 'Full Name',
|
||||
placeholder: 'Enter your name',
|
||||
value: name,
|
||||
oninput: (e) => name(e.target.value)
|
||||
});
|
||||
};
|
||||
$mount(BasicDemo, basicTarget);
|
||||
}
|
||||
|
||||
// 2. With Icon
|
||||
const iconTarget = document.querySelector('#demo-icon');
|
||||
if (iconTarget && !iconTarget.hasChildNodes()) {
|
||||
const IconDemo = () => {
|
||||
const email = $('');
|
||||
return Input({
|
||||
label: 'Email',
|
||||
type: 'email',
|
||||
icon: Icons.iconMail,
|
||||
value: email,
|
||||
oninput: (e) => email(e.target.value)
|
||||
});
|
||||
};
|
||||
$mount(IconDemo, iconTarget);
|
||||
}
|
||||
|
||||
// 3. Password with Toggle
|
||||
const passwordTarget = document.querySelector('#demo-password');
|
||||
if (passwordTarget && !passwordTarget.hasChildNodes()) {
|
||||
const PasswordDemo = () => {
|
||||
const password = $('');
|
||||
return Input({
|
||||
label: 'Password',
|
||||
type: 'password',
|
||||
value: password,
|
||||
oninput: (e) => password(e.target.value)
|
||||
});
|
||||
};
|
||||
$mount(PasswordDemo, passwordTarget);
|
||||
}
|
||||
|
||||
// 4. With Tooltip
|
||||
const tooltipTarget = document.querySelector('#demo-tooltip');
|
||||
if (tooltipTarget && !tooltipTarget.hasChildNodes()) {
|
||||
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, tooltipTarget);
|
||||
}
|
||||
|
||||
// 5. Error State
|
||||
const errorTarget = document.querySelector('#demo-error');
|
||||
if (errorTarget && !errorTarget.hasChildNodes()) {
|
||||
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, errorTarget);
|
||||
}
|
||||
|
||||
// 6. Disabled State
|
||||
const disabledTarget = document.querySelector('#demo-disabled');
|
||||
if (disabledTarget && !disabledTarget.hasChildNodes()) {
|
||||
const DisabledDemo = () => {
|
||||
return Input({
|
||||
label: 'Username',
|
||||
value: 'john.doe',
|
||||
disabled: true
|
||||
});
|
||||
};
|
||||
$mount(DisabledDemo, disabledTarget);
|
||||
}
|
||||
|
||||
// 7. All Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
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, variantsTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initInputExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initInputExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
697
docs/components/radio.md
Normal file
697
docs/components/radio.md
Normal file
@@ -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<string>` | `-` | Selected value (for group) |
|
||||
| `radioValue` | `string` | `-` | Value of this radio button |
|
||||
| `tooltip` | `string` | `-` | Tooltip text on hover |
|
||||
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||
| `onclick` | `function` | `-` | Click event handler |
|
||||
|
||||
## Live Examples
|
||||
|
||||
### Basic Radio Group
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-tooltip" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-disabled" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-payment" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-dynamic" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initRadioExamples = () => {
|
||||
|
||||
// 1. Basic Radio Group
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
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, basicTarget);
|
||||
}
|
||||
|
||||
// 2. With Tooltip
|
||||
const tooltipTarget = document.querySelector('#demo-tooltip');
|
||||
if (tooltipTarget && !tooltipTarget.hasChildNodes()) {
|
||||
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, tooltipTarget);
|
||||
}
|
||||
|
||||
// 3. Disabled State
|
||||
const disabledTarget = document.querySelector('#demo-disabled');
|
||||
if (disabledTarget && !disabledTarget.hasChildNodes()) {
|
||||
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, disabledTarget);
|
||||
}
|
||||
|
||||
// 4. Reactive Preview
|
||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
||||
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, reactiveTarget);
|
||||
}
|
||||
|
||||
// 5. Payment Method Selection
|
||||
const paymentTarget = document.querySelector('#demo-payment');
|
||||
if (paymentTarget && !paymentTarget.hasChildNodes()) {
|
||||
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, paymentTarget);
|
||||
}
|
||||
|
||||
// 6. All Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
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, variantsTarget);
|
||||
}
|
||||
|
||||
// 7. Dynamic Options
|
||||
const dynamicTarget = document.querySelector('#demo-dynamic');
|
||||
if (dynamicTarget && !dynamicTarget.hasChildNodes()) {
|
||||
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, dynamicTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initRadioExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initRadioExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
0
docs/components/rating.md
Normal file
0
docs/components/rating.md
Normal file
366
docs/components/select.md
Normal file
366
docs/components/select.md
Normal file
@@ -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<string>` | `''` | Selected value |
|
||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
||||
| `onchange` | `function` | `-` | Change event handler |
|
||||
|
||||
## Live Examples
|
||||
|
||||
### Basic Select
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-disabled" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-dynamic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```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');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initSelectExamples = () => {
|
||||
|
||||
// 1. Basic Select
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
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, basicTarget);
|
||||
}
|
||||
|
||||
// 2. Reactive Display
|
||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
||||
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, reactiveTarget);
|
||||
}
|
||||
|
||||
// 3. Disabled State
|
||||
const disabledTarget = document.querySelector('#demo-disabled');
|
||||
if (disabledTarget && !disabledTarget.hasChildNodes()) {
|
||||
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, disabledTarget);
|
||||
}
|
||||
|
||||
// 4. Dynamic Options
|
||||
const dynamicTarget = document.querySelector('#demo-dynamic');
|
||||
if (dynamicTarget && !dynamicTarget.hasChildNodes()) {
|
||||
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, dynamicTarget);
|
||||
}
|
||||
|
||||
// 5. All Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
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, variantsTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initSelectExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initSelectExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
0
docs/components/swap.md
Normal file
0
docs/components/swap.md
Normal file
Reference in New Issue
Block a user