Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0697b4b4b7 | |||
| 00b9b8f911 | |||
| d0b55ff354 | |||
| 3c1f14415e | |||
| c4ebf5847b | |||
| 290c87cec3 | |||
| 45e82b20e5 | |||
| c08f001a80 | |||
| ebc4dc2d3b | |||
| 071a215393 | |||
| 294547fc56 | |||
| f9095332eb | |||
| d68b5a74e0 | |||
| 4bc640b2c3 | |||
| 011aad6f2f | |||
|
|
f39359ef67 | ||
| 9fefa6dcb1 | |||
|
|
8f9d01e766 | ||
|
|
7956d2b9b0 | ||
| 3f8273523c | |||
|
|
7d2af57ac1 | ||
|
|
a677f5d00b | ||
|
|
95042a2e36 | ||
|
|
ede3caa7d6 | ||
|
|
78bac75fd5 | ||
|
|
1478a7d63d | ||
| bd63afa23b | |||
| 00d114630d |
155
Readme.md
155
Readme.md
@@ -1,6 +1,8 @@
|
|||||||
# SigPro UI (W.I.P.)
|
# SigPro UI
|
||||||
|
|
||||||
**SigPro UI** is a lightweight, ultra-fast, and reactive component library built for the **SigPro** reactivity core. It provides a set of high-quality, accessible, and themeable UI components leveraging the power of **Tailwind CSS v4** and **daisyUI v5**.
|
**SigPro UI** is a lightweight, ultra-fast, and reactive component library built for the **SigPro** reactivity core. It provides a set of high-quality, accessible, and themeable UI components with **zero external dependencies** - everything is included.
|
||||||
|
|
||||||
|
**SigPro UI** is a complete, self-contained UI library + reactive core in under **45KB gzip** (<15KB JS + <30KB CSS). 🎉
|
||||||
|
|
||||||
Unlike heavy frameworks, SigPro UI focuses on a **"Zero-Build"** philosophy, allowing you to build complex reactive interfaces with a functional, declarative syntax that runs natively in the browser.
|
Unlike heavy frameworks, SigPro UI focuses on a **"Zero-Build"** philosophy, allowing you to build complex reactive interfaces with a functional, declarative syntax that runs natively in the browser.
|
||||||
|
|
||||||
@@ -8,96 +10,73 @@ Unlike heavy frameworks, SigPro UI focuses on a **"Zero-Build"** philosophy, all
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* **Signals-Based Reactivity**: Powered by SigPro for granular DOM updates.
|
- **Signals-Based Reactivity**: Powered by SigPro for granular DOM updates
|
||||||
* **DaisyUI v5 Integration**: Beautiful, semantic components out of the box.
|
- **Self-Contained Styling**: Full CSS included - no external frameworks needed
|
||||||
* **Tree Shaking Ready**: Import only what you need.
|
- **Built on daisyUI v5**: Modern, utility-first styling out of the box
|
||||||
* **Zero-Import Option**: Inject all components into the global scope with one command.
|
- **Tree Shaking Ready**: Import only what you need
|
||||||
* **Lightweight**: Minimal footprint with a focus on performance.
|
- **Zero-Import Option**: Inject all components into the global scope with one command
|
||||||
|
- **Lightweight**: Minimal footprint with everything bundled
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Prerequisites
|
## Installation
|
||||||
|
|
||||||
To use SigPro UI, your project must include:
|
### Via NPM
|
||||||
|
|
||||||
1. **SigPro Core**: `npm install sigpro` (or via CDN).
|
```bash
|
||||||
2. **Tailwind CSS v4**: For utility-first styling.
|
|
||||||
3. **daisyUI v5**: The component engine for Tailwind.
|
|
||||||
|
|
||||||
|
|
||||||
## 1. Prerequisites & Installation
|
|
||||||
SigPro UI is built as an extension of the SigPro reactivity system. You need to install the core library first.
|
|
||||||
|
|
||||||
### Step 1: Install SigPro Core
|
|
||||||
|
|
||||||
```Bash
|
|
||||||
bun add sigpro
|
|
||||||
# or
|
|
||||||
npm install sigpro
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2: Install SigPro UI
|
|
||||||
|
|
||||||
```Bash
|
|
||||||
bun add sigpro-ui
|
|
||||||
# or
|
|
||||||
npm install sigpro-ui
|
npm install sigpro-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
### Via CDN (Browser)
|
### Via CDN (Browser)
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://unpkg.com/sigpro"></script>
|
<!-- SigPro UI with styles included -->
|
||||||
<script src="https://unpkg.com/sigpro-ui"></script>
|
<script src="https://unpkg.com/sigpro-ui"></script>
|
||||||
|
<link href="https://unpkg.com/sigpro-ui/css" rel="stylesheet">
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**That's it!** No additional CSS files, no configuration, no build step. SigPro core is included internally.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Styling Setup (Tailwind CSS v4)
|
## Usage
|
||||||
SigPro UI components rely on **Tailwind CSS** and **daisyUI** for styling. You don't need a complex tailwind.config.js; simply configure your main CSS entry point.
|
|
||||||
|
|
||||||
Create or edit your global CSS file (e.g., style.css):
|
You can use SigPro UI in two ways: **Modular** (Recommended) or **Global** (Fastest for prototyping).
|
||||||
|
|
||||||
```css
|
|
||||||
/* src/style.css */
|
|
||||||
@import "tailwindcss";
|
|
||||||
@plugin "daisyui";
|
|
||||||
|
|
||||||
/* Optional: Your custom theme overrides */
|
|
||||||
:root {
|
|
||||||
--primary: #570df8;
|
|
||||||
--secondary: #f000b8;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
---
|
|
||||||
## Setup & Usage
|
|
||||||
|
|
||||||
You can use SigPro UI in two ways: **Modular** (Recommended for production) or **Global** (Fastest for prototyping).
|
|
||||||
|
|
||||||
### 1. Modular Approach (Tree Shaking)
|
### 1. Modular Approach (Tree Shaking)
|
||||||
Import only the components you need. This keeps your bundle small.
|
|
||||||
|
Import only the components you need:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { $, $mount } from "sigpro";
|
import { $, Mount, Button, Modal, Input, Alert } from "sigpro-ui";
|
||||||
import { Button, Modal, Table } from "sigpro-ui";
|
import "sigpro-ui/css";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const show = $(false);
|
const show = $(false);
|
||||||
return Button({ onclick: () => show(true) }, "Open Modal");
|
|
||||||
|
return Button(
|
||||||
|
{
|
||||||
|
class: "btn-primary",
|
||||||
|
onclick: () => show(true)
|
||||||
|
},
|
||||||
|
"Open Modal"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(App, "#app");
|
Mount(App, "#app");
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Global Approach (Zero-Import)
|
### 2. Global Approach (Zero-Import)
|
||||||
Inject all components and utilities into the `window` object. This makes all components available everywhere without manual imports.
|
|
||||||
|
Inject all components into the `window` object:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import SigproUI from "sigpro-ui";
|
import "sigpro-ui";
|
||||||
|
import "sigpro-ui/css";
|
||||||
|
|
||||||
// Injects Button, Table, Input, Icons, Utils, etc. into window
|
// All components (Button, Table, Input, Alert, etc.) are now globally available.
|
||||||
SigproUI.install();
|
// No need to import anything else!
|
||||||
|
|
||||||
// Now you can use them directly anywhere:
|
|
||||||
const myApp = () => Table({ items: [], columns: [] });
|
const myApp = () => Table({ items: [], columns: [] });
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -106,8 +85,8 @@ const myApp = () => Table({ items: [], columns: [] });
|
|||||||
## Basic Example
|
## Basic Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { $ } from "sigpro";
|
import { $, Mount, Button, Toast, Div, H1 } from "sigpro-ui";
|
||||||
import { Button, Toast, Div, H1 } from "sigpro-ui";
|
import "sigpro-ui/css";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const count = $(0);
|
const count = $(0);
|
||||||
@@ -124,23 +103,57 @@ const App = () => {
|
|||||||
}, () => `Clicks: ${count()}`)
|
}, () => `Clicks: ${count()}`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mount(App, "#app");
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Components Included
|
## What's Included?
|
||||||
|
|
||||||
| Category | Components |
|
### Core Functions (from SigPro)
|
||||||
| :--- | :--- |
|
- `$()` - Reactive signals
|
||||||
| **Form** | `Input`, `Select`, `Checkbox`, `Radio`, `Range`, `Datepicker`, `Colorpicker`, `Autocomplete`, `Rating` |
|
- `Watch()` - Watch reactive dependencies
|
||||||
| **Data** | `Table`, `List`, `Stat`, `Timeline`, `Badge`, `Indicator` |
|
- `Tag()` - Create HTML elements with reactivity
|
||||||
| **Layout** | `Navbar`, `Menu`, `Drawer`, `Fieldset`, `Stack`, `Tabs`, `Accordion` |
|
- `If()` - Conditional rendering
|
||||||
| **Feedback** | `Alert`, `Modal`, `Toast`, `Loading`, `Tooltip` |
|
- `For()` - List rendering
|
||||||
| **Interaction** | `Button`, `Dropdown`, `Swap`, `Fab` |
|
- `Router()` - Hash-based routing
|
||||||
|
- `Mount()` - Mount components to DOM
|
||||||
|
|
||||||
|
Explore [SigPro Core Docs](https://natxocc.github.io/sigpro/#/) for more information.
|
||||||
|
|
||||||
|
### UI Components
|
||||||
|
- `Button`, `Input`, `Select`, `Checkbox`, `Radio`
|
||||||
|
- `Modal`, `Alert`, `Toast`, `Tooltip`
|
||||||
|
- `Table`, `List`, `Badge`, `Stat`, `Timeline`
|
||||||
|
- `Tabs`, `Accordion`, `Dropdown`, `Drawer`
|
||||||
|
- `Datepicker`, `Colorpicker`, `Autocomplete`, `Rating`
|
||||||
|
- `Fileinput`, `Fab`, `Swap`, `Indicator`
|
||||||
|
- And 30+ more!
|
||||||
|
|
||||||
|
### Utilities
|
||||||
|
- `tt()` - i18n translation function (ES/EN)
|
||||||
|
- `Locale()` - Set global language
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Language Support
|
||||||
|
|
||||||
|
Built-in i18n with Spanish and English:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { tt, Locale } from "sigpro-ui";
|
||||||
|
|
||||||
|
// Change locale (default is 'es')
|
||||||
|
Locale('en');
|
||||||
|
|
||||||
|
// Use translations
|
||||||
|
const closeButton = Button({}, tt('close')());
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT © 2026 **SigPro Team**.
|
MIT © 2026 **SigPro Team**
|
||||||
*Engineered for speed, designed for clarity, built for the modern web.*
|
*Engineered for speed, designed for clarity, built for the modern web.*
|
||||||
|
|||||||
553
css/sigpro.css
553
css/sigpro.css
@@ -53,7 +53,6 @@
|
|||||||
--radius-3xl: 1.5rem;
|
--radius-3xl: 1.5rem;
|
||||||
--drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / 0.15);
|
--drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / 0.15);
|
||||||
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
--blur-sm: 8px;
|
|
||||||
--blur-3xl: 64px;
|
--blur-3xl: 64px;
|
||||||
--default-transition-duration: 150ms;
|
--default-transition-duration: 150ms;
|
||||||
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
@@ -1418,6 +1417,89 @@
|
|||||||
.visible {
|
.visible {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
.tabs-lift {
|
||||||
|
@layer daisyui.l1.l2 {
|
||||||
|
--tabs-height: auto;
|
||||||
|
--tabs-direction: row;
|
||||||
|
> .tab {
|
||||||
|
--tab-border: 0 0 var(--border) 0;
|
||||||
|
--tab-radius-ss: var(--tab-radius-limit);
|
||||||
|
--tab-radius-se: var(--tab-radius-limit);
|
||||||
|
--tab-radius-es: 0;
|
||||||
|
--tab-radius-ee: 0;
|
||||||
|
--tab-paddings: var(--border) var(--tab-p) 0 var(--tab-p);
|
||||||
|
--tab-border-colors: #0000 #0000 var(--tab-border-color) #0000;
|
||||||
|
--tab-corner-width: calc(100% + var(--tab-radius-limit) * 2);
|
||||||
|
--tab-corner-height: var(--tab-radius-limit);
|
||||||
|
--tab-corner-position: top left, top right;
|
||||||
|
border-width: var(--tab-border);
|
||||||
|
border-start-start-radius: var(--tab-radius-ss);
|
||||||
|
border-start-end-radius: var(--tab-radius-se);
|
||||||
|
border-end-start-radius: var(--tab-radius-es);
|
||||||
|
border-end-end-radius: var(--tab-radius-ee);
|
||||||
|
padding: var(--tab-paddings);
|
||||||
|
border-color: var(--tab-border-colors);
|
||||||
|
&:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]):not( .tab-disabled, [disabled] ), &:is(input:checked, label:has(:checked)) {
|
||||||
|
--tab-border: var(--border) var(--border) 0 var(--border);
|
||||||
|
--tab-border-colors: var(--tab-border-color) var(--tab-border-color) #0000
|
||||||
|
var(--tab-border-color);
|
||||||
|
--tab-paddings: 0 calc(var(--tab-p) - var(--border)) var(--border)
|
||||||
|
calc(var(--tab-p) - var(--border));
|
||||||
|
--tab-inset: auto auto 0 auto;
|
||||||
|
--radius-start: radial-gradient(circle at top left, var(--tab-radius-grad));
|
||||||
|
--radius-end: radial-gradient(circle at top right, var(--tab-radius-grad));
|
||||||
|
background-color: var(--tab-bg);
|
||||||
|
&:before {
|
||||||
|
z-index: 1;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
width: var(--tab-corner-width);
|
||||||
|
height: var(--tab-corner-height);
|
||||||
|
background-position: var(--tab-corner-position);
|
||||||
|
background-image: var(--radius-start), var(--radius-end);
|
||||||
|
background-size: var(--tab-radius-limit) var(--tab-radius-limit);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
inset: var(--tab-inset);
|
||||||
|
}
|
||||||
|
&:first-child:before {
|
||||||
|
--radius-start: none;
|
||||||
|
}
|
||||||
|
[dir="rtl"] &:first-child:before {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
&:last-child:before {
|
||||||
|
--radius-end: none;
|
||||||
|
}
|
||||||
|
[dir="rtl"] &:last-child:before {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:has(> .tab-content) {
|
||||||
|
> .tab:first-child {
|
||||||
|
&:not(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) {
|
||||||
|
--tab-border-colors: var(--tab-border-color) var(--tab-border-color) #0000
|
||||||
|
var(--tab-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .tab-content {
|
||||||
|
--tabcontent-margin: calc(-1 * var(--border)) 0 0 0;
|
||||||
|
--tabcontent-radius-ss: 0;
|
||||||
|
--tabcontent-radius-se: var(--radius-box);
|
||||||
|
--tabcontent-radius-es: var(--radius-box);
|
||||||
|
--tabcontent-radius-ee: var(--radius-box);
|
||||||
|
}
|
||||||
|
:checked, label:has(:checked), :is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) {
|
||||||
|
& + .tab-content {
|
||||||
|
&:nth-child(1), &:nth-child(n + 3) {
|
||||||
|
--tabcontent-radius-ss: var(--radius-box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.list {
|
.list {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -2026,6 +2108,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.tabs-border {
|
||||||
|
@layer daisyui.l1.l2 {
|
||||||
|
> .tab {
|
||||||
|
--tab-border-color: #0000 #0000 var(--tab-border-color) #0000;
|
||||||
|
position: relative;
|
||||||
|
border-radius: var(--radius-field);
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
background-color: var(--tab-border-color);
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
width: 80%;
|
||||||
|
height: 3px;
|
||||||
|
border-radius: var(--radius-field);
|
||||||
|
bottom: 0;
|
||||||
|
left: 10%;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
&:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]):not( .tab-disabled, [disabled] ), &:is(input:checked), &:is(label:has(:checked)) {
|
||||||
|
&:before {
|
||||||
|
--tab-border-color: currentColor;
|
||||||
|
border-top: 3px solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.select {
|
.select {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
border: var(--border) solid #0000;
|
border: var(--border) solid #0000;
|
||||||
@@ -2308,17 +2416,6 @@
|
|||||||
transition: background-color 0.2s ease-out;
|
transition: background-color 0.2s ease-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
padding: 0;
|
|
||||||
margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip-path: inset(50%);
|
|
||||||
white-space: nowrap;
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
||||||
.menu-horizontal {
|
.menu-horizontal {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -3283,21 +3380,6 @@
|
|||||||
border-end-end-radius: var(--tabcontent-radius-ee);
|
border-end-end-radius: var(--tabcontent-radius-ee);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.order-0 {
|
|
||||||
order: 0;
|
|
||||||
}
|
|
||||||
.order-1 {
|
|
||||||
order: 1;
|
|
||||||
}
|
|
||||||
.order-2 {
|
|
||||||
order: 2;
|
|
||||||
}
|
|
||||||
.order-3 {
|
|
||||||
order: 3;
|
|
||||||
}
|
|
||||||
.order-4 {
|
|
||||||
order: 4;
|
|
||||||
}
|
|
||||||
.timeline-end {
|
.timeline-end {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
@@ -3721,9 +3803,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mx-1 {
|
|
||||||
margin-inline: calc(var(--spacing) * 1);
|
|
||||||
}
|
|
||||||
.mx-auto {
|
.mx-auto {
|
||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
@@ -4293,69 +4372,6 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.fab-flower {
|
|
||||||
@layer daisyui.l1.l2.l3 {
|
|
||||||
display: grid;
|
|
||||||
--position: 0rem;
|
|
||||||
> *:nth-child(-n + 2) {
|
|
||||||
--position: 0rem;
|
|
||||||
}
|
|
||||||
> * {
|
|
||||||
grid-area: 1/1;
|
|
||||||
--degree: 180deg;
|
|
||||||
--flip-degree: calc(180deg - var(--degree));
|
|
||||||
transform: translateX(calc(cos(var(--degree)) * var(--position))) translateY(calc(sin(var(--degree)) * -1 * var(--position)));
|
|
||||||
[dir="rtl"] & {
|
|
||||||
transform: translateX(calc(cos(var(--flip-degree)) * var(--position))) translateY(calc(sin(var(--flip-degree)) * -1 * var(--position)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> :nth-child(n + 7) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
&:has(:nth-child(3)) {
|
|
||||||
--position: 140%;
|
|
||||||
> :nth-child(3) {
|
|
||||||
--degree: 135deg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:has(:nth-child(4)) {
|
|
||||||
--position: 140%;
|
|
||||||
> :nth-child(3) {
|
|
||||||
--degree: 165deg;
|
|
||||||
}
|
|
||||||
> :nth-child(4) {
|
|
||||||
--degree: 105deg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:has(:nth-child(5)) {
|
|
||||||
--position: 180%;
|
|
||||||
> :nth-child(3) {
|
|
||||||
--degree: 180deg;
|
|
||||||
}
|
|
||||||
> :nth-child(4) {
|
|
||||||
--degree: 135deg;
|
|
||||||
}
|
|
||||||
> :nth-child(5) {
|
|
||||||
--degree: 90deg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:has(:nth-child(6)) {
|
|
||||||
--position: 220%;
|
|
||||||
> :nth-child(3) {
|
|
||||||
--degree: 180deg;
|
|
||||||
}
|
|
||||||
> :nth-child(4) {
|
|
||||||
--degree: 150deg;
|
|
||||||
}
|
|
||||||
> :nth-child(5) {
|
|
||||||
--degree: 120deg;
|
|
||||||
}
|
|
||||||
> :nth-child(6) {
|
|
||||||
--degree: 90deg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.fieldset-label {
|
.fieldset-label {
|
||||||
@layer daisyui.l1.l2.l3 {
|
@layer daisyui.l1.l2.l3 {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -4528,15 +4544,6 @@
|
|||||||
width: calc(var(--spacing) * 6);
|
width: calc(var(--spacing) * 6);
|
||||||
height: calc(var(--spacing) * 6);
|
height: calc(var(--spacing) * 6);
|
||||||
}
|
}
|
||||||
.h-3 {
|
|
||||||
height: calc(var(--spacing) * 3);
|
|
||||||
}
|
|
||||||
.h-4 {
|
|
||||||
height: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.h-5 {
|
|
||||||
height: calc(var(--spacing) * 5);
|
|
||||||
}
|
|
||||||
.h-8 {
|
.h-8 {
|
||||||
height: calc(var(--spacing) * 8);
|
height: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
@@ -4582,21 +4589,12 @@
|
|||||||
.max-h-60 {
|
.max-h-60 {
|
||||||
max-height: calc(var(--spacing) * 60);
|
max-height: calc(var(--spacing) * 60);
|
||||||
}
|
}
|
||||||
.max-h-64 {
|
|
||||||
max-height: calc(var(--spacing) * 64);
|
|
||||||
}
|
|
||||||
.max-h-96 {
|
.max-h-96 {
|
||||||
max-height: calc(var(--spacing) * 96);
|
max-height: calc(var(--spacing) * 96);
|
||||||
}
|
}
|
||||||
.min-h-0 {
|
.min-h-0 {
|
||||||
min-height: calc(var(--spacing) * 0);
|
min-height: calc(var(--spacing) * 0);
|
||||||
}
|
}
|
||||||
.min-h-\[100px\] {
|
|
||||||
min-height: 100px;
|
|
||||||
}
|
|
||||||
.min-h-\[200px\] {
|
|
||||||
min-height: 200px;
|
|
||||||
}
|
|
||||||
.min-h-\[300px\] {
|
.min-h-\[300px\] {
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
}
|
}
|
||||||
@@ -4620,35 +4618,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.loading-lg {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
width: calc(var(--size-selector, 0.25rem) * 7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-md {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
width: calc(var(--size-selector, 0.25rem) * 6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-sm {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
width: calc(var(--size-selector, 0.25rem) * 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-xs {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
width: calc(var(--size-selector, 0.25rem) * 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.w-3 {
|
|
||||||
width: calc(var(--spacing) * 3);
|
|
||||||
}
|
|
||||||
.w-4 {
|
|
||||||
width: calc(var(--spacing) * 4);
|
|
||||||
}
|
|
||||||
.w-5 {
|
|
||||||
width: calc(var(--spacing) * 5);
|
|
||||||
}
|
|
||||||
.w-8 {
|
.w-8 {
|
||||||
width: calc(var(--spacing) * 8);
|
width: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
@@ -4709,9 +4678,6 @@
|
|||||||
.max-w-md {
|
.max-w-md {
|
||||||
max-width: var(--container-md);
|
max-width: var(--container-md);
|
||||||
}
|
}
|
||||||
.min-w-48 {
|
|
||||||
min-width: calc(var(--spacing) * 48);
|
|
||||||
}
|
|
||||||
.min-w-\[4rem\] {
|
.min-w-\[4rem\] {
|
||||||
min-width: 4rem;
|
min-width: 4rem;
|
||||||
}
|
}
|
||||||
@@ -4724,9 +4690,6 @@
|
|||||||
.min-w-\[600px\] {
|
.min-w-\[600px\] {
|
||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
}
|
}
|
||||||
.min-w-max {
|
|
||||||
min-width: max-content;
|
|
||||||
}
|
|
||||||
.flex-1 {
|
.flex-1 {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@@ -4816,24 +4779,6 @@
|
|||||||
.animate-pulse {
|
.animate-pulse {
|
||||||
animation: var(--animate-pulse);
|
animation: var(--animate-pulse);
|
||||||
}
|
}
|
||||||
.link {
|
|
||||||
@layer daisyui.l1.l2.l3 {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration-line: underline;
|
|
||||||
&:focus {
|
|
||||||
--tw-outline-style: none;
|
|
||||||
outline-style: none;
|
|
||||||
@media (forced-colors: active) {
|
|
||||||
outline: 2px solid transparent;
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:focus-visible {
|
|
||||||
outline: 2px solid currentColor;
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cursor-help {
|
.cursor-help {
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
@@ -5227,12 +5172,6 @@
|
|||||||
.bg-base-100 {
|
.bg-base-100 {
|
||||||
background-color: var(--color-base-100);
|
background-color: var(--color-base-100);
|
||||||
}
|
}
|
||||||
.bg-base-100\/30 {
|
|
||||||
background-color: var(--color-base-100);
|
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
|
||||||
background-color: color-mix(in oklab, var(--color-base-100) 30%, transparent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.bg-base-200 {
|
.bg-base-200 {
|
||||||
background-color: var(--color-base-200);
|
background-color: var(--color-base-200);
|
||||||
}
|
}
|
||||||
@@ -5379,31 +5318,6 @@
|
|||||||
--tw-gradient-to: var(--color-secondary);
|
--tw-gradient-to: var(--color-secondary);
|
||||||
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
|
||||||
}
|
}
|
||||||
.loading-ball {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cellipse cx='12' cy='5' rx='4' ry='4'%3E%3Canimate attributeName='cy' values='5;20;20.5;20;5' keyTimes='0;0.469;0.5;0.531;1' dur='.8s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1'/%3E%3Canimate attributeName='rx' values='4;4;4.8;4;4' keyTimes='0;0.469;0.5;0.531;1' dur='.8s' repeatCount='indefinite'/%3E%3Canimate attributeName='ry' values='4;4;3;4;4' keyTimes='0;0.469;0.5;0.531;1' dur='.8s' repeatCount='indefinite'/%3E%3C/ellipse%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-bars {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='1' y='1' width='6' height='22'%3E%3Canimate attributeName='y' values='1;5;1' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite'/%3E%3Canimate attributeName='height' values='22;14;22' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite'/%3E%3Canimate attributeName='opacity' values='1;0.2;1' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite'/%3E%3C/rect%3E%3Crect x='9' y='1' width='6' height='22'%3E%3Canimate attributeName='y' values='1;5;1' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite' begin='-0.65s'/%3E%3Canimate attributeName='height' values='22;14;22' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite' begin='-0.65s'/%3E%3Canimate attributeName='opacity' values='1;0.2;1' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite' begin='-0.65s'/%3E%3C/rect%3E%3Crect x='17' y='1' width='6' height='22'%3E%3Canimate attributeName='y' values='1;5;1' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite' begin='-0.5s'/%3E%3Canimate attributeName='height' values='22;14;22' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite' begin='-0.5s'/%3E%3Canimate attributeName='opacity' values='1;0.2;1' keyTimes='0;0.938;1' dur='.8s' repeatCount='indefinite' begin='-0.5s'/%3E%3C/rect%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-dots {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='4' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1'/%3E%3C/circle%3E%3Ccircle cx='12' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1' begin='0.1s'/%3E%3C/circle%3E%3Ccircle cx='20' cy='12' r='3'%3E%3Canimate attributeName='cy' values='12;6;12;12' keyTimes='0;0.286;0.571;1' dur='1.05s' repeatCount='indefinite' keySplines='.33,0,.66,.33;.33,.66,.66,1' begin='0.2s'/%3E%3C/circle%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-infinity {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' style='shape-rendering:auto;' width='200px' height='200px' viewBox='0 0 100 100' preserveAspectRatio='xMidYMid'%3E%3Cpath fill='none' stroke='black' stroke-width='10' stroke-dasharray='205.271 51.318' d='M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z' stroke-linecap='round' style='transform:scale(0.8);transform-origin:50px 50px'%3E%3Canimate attributeName='stroke-dashoffset' repeatCount='indefinite' dur='2s' keyTimes='0;1' values='0;256.589'/%3E%3C/path%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-ring {
|
|
||||||
@layer daisyui.l1.l2 {
|
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg width='44' height='44' viewBox='0 0 44 44' xmlns='http://www.w3.org/2000/svg' stroke='white'%3E%3Cg fill='none' fill-rule='evenodd' stroke-width='2'%3E%3Ccircle cx='22' cy='22' r='1'%3E%3Canimate attributeName='r' begin='0s' dur='1.8s' values='1;20' calcMode='spline' keyTimes='0;1' keySplines='0.165,0.84,0.44,1' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-opacity' begin='0s' dur='1.8s' values='1;0' calcMode='spline' keyTimes='0;1' keySplines='0.3,0.61,0.355,1' repeatCount='indefinite'/%3E%3C/circle%3E%3Ccircle cx='22' cy='22' r='1'%3E%3Canimate attributeName='r' begin='-0.9s' dur='1.8s' values='1;20' calcMode='spline' keyTimes='0;1' keySplines='0.165,0.84,0.44,1' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-opacity' begin='-0.9s' dur='1.8s' values='1;0' calcMode='spline' keyTimes='0;1' keySplines='0.3,0.61,0.355,1' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loading-spinner {
|
.loading-spinner {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");
|
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E");
|
||||||
@@ -5435,9 +5349,6 @@
|
|||||||
.bg-clip-text {
|
.bg-clip-text {
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
}
|
}
|
||||||
.object-contain {
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
.checkbox-lg {
|
.checkbox-lg {
|
||||||
@layer daisyui.l1.l2 {
|
@layer daisyui.l1.l2 {
|
||||||
padding: 0.3125rem;
|
padding: 0.3125rem;
|
||||||
@@ -5658,9 +5569,6 @@
|
|||||||
.pt-2 {
|
.pt-2 {
|
||||||
padding-top: calc(var(--spacing) * 2);
|
padding-top: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
.pt-6 {
|
|
||||||
padding-top: calc(var(--spacing) * 6);
|
|
||||||
}
|
|
||||||
.pr-10 {
|
.pr-10 {
|
||||||
padding-right: calc(var(--spacing) * 10);
|
padding-right: calc(var(--spacing) * 10);
|
||||||
}
|
}
|
||||||
@@ -5794,10 +5702,6 @@
|
|||||||
.text-\[10px\] {
|
.text-\[10px\] {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
.leading-20 {
|
|
||||||
--tw-leading: calc(var(--spacing) * 20);
|
|
||||||
line-height: calc(var(--spacing) * 20);
|
|
||||||
}
|
|
||||||
.leading-relaxed {
|
.leading-relaxed {
|
||||||
--tw-leading: var(--leading-relaxed);
|
--tw-leading: var(--leading-relaxed);
|
||||||
line-height: var(--leading-relaxed);
|
line-height: var(--leading-relaxed);
|
||||||
@@ -6225,11 +6129,6 @@
|
|||||||
.filter {
|
.filter {
|
||||||
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
||||||
}
|
}
|
||||||
.backdrop-blur-sm {
|
|
||||||
--tw-backdrop-blur: blur(var(--blur-sm));
|
|
||||||
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
|
|
||||||
backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
|
|
||||||
}
|
|
||||||
.transition-all {
|
.transition-all {
|
||||||
transition-property: all;
|
transition-property: all;
|
||||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||||
@@ -6877,12 +6776,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.focus\:outline-none {
|
|
||||||
&:focus {
|
|
||||||
--tw-outline-style: none;
|
|
||||||
outline-style: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.active\:scale-95 {
|
.active\:scale-95 {
|
||||||
&:active {
|
&:active {
|
||||||
--tw-scale-x: 95%;
|
--tw-scale-x: 95%;
|
||||||
@@ -6964,6 +6857,88 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.input, .select, .textarea {
|
||||||
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
&:focus, &:focus-within {
|
||||||
|
outline: 2px solid transparent !important;
|
||||||
|
outline-offset: 2px !important;
|
||||||
|
}
|
||||||
|
&:hover:not(:focus) {
|
||||||
|
background-color: oklch(from var(--color-base-100) calc(l - 0.03) c h);
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
--focus-color: var(--color-primary);
|
||||||
|
&[class*="-secondary"] {
|
||||||
|
--focus-color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
&[class*="-accent"] {
|
||||||
|
--focus-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
&[class*="-neutral"] {
|
||||||
|
--focus-color: var(--color-neutral);
|
||||||
|
}
|
||||||
|
&[class*="-ghost"] {
|
||||||
|
--focus-color: var(--color-base-content);
|
||||||
|
}
|
||||||
|
&[class*="-info"] {
|
||||||
|
--focus-color: var(--color-info);
|
||||||
|
}
|
||||||
|
&[class*="-success"] {
|
||||||
|
--focus-color: var(--color-success);
|
||||||
|
}
|
||||||
|
&[class*="-warning"] {
|
||||||
|
--focus-color: var(--color-warning);
|
||||||
|
}
|
||||||
|
&[class*="-error"] {
|
||||||
|
--focus-color: var(--color-error);
|
||||||
|
}
|
||||||
|
background-color: oklch(from var(--focus-color) l c h / 0.05);
|
||||||
|
border-color: var(--focus-color);
|
||||||
|
box-shadow: 0 0 0 4px oklch(from var(--focus-color) l c h / 0.25);
|
||||||
|
&[class*="-ghost"] {
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.floating-label span {
|
||||||
|
color: oklch(30% 0.01 260);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.floating-label:focus-within span {
|
||||||
|
color: oklch(25% 0.02 260);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
.floating-label:has(input:not(:placeholder-shown)) span {
|
||||||
|
color: oklch(28% 0.01 260);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
.collapse .collapse-content {
|
||||||
|
transform: scaleY(0);
|
||||||
|
transform-origin: top;
|
||||||
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.collapse:has(input:checked) .collapse-content {
|
||||||
|
transform: scaleY(1);
|
||||||
|
height: auto;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.tab-content-inner {
|
||||||
|
animation: tabFadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transform-origin: top;
|
||||||
|
}
|
||||||
|
@keyframes tabFadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scaleY(0.95);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scaleY(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@layer base {
|
@layer base {
|
||||||
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] {
|
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] {
|
||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
@@ -7210,6 +7185,107 @@
|
|||||||
background-position-x: -115%;
|
background-position-x: -115%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@layer base {
|
||||||
|
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme="light"] {
|
||||||
|
color-scheme: light;
|
||||||
|
--color-base-100: oklch(100% 0 0);
|
||||||
|
--color-base-200: oklch(98% 0 0);
|
||||||
|
--color-base-300: oklch(92% 0 0);
|
||||||
|
--color-base-content: oklch(25% 0.006 285);
|
||||||
|
--color-primary: oklch(25% 0.006 285);
|
||||||
|
--color-primary-content: oklch(98% 0 0);
|
||||||
|
--color-secondary: oklch(55% 0.046 257.417);
|
||||||
|
--color-secondary-content: oklch(98% 0 0);
|
||||||
|
--color-accent: oklch(96% 0 0);
|
||||||
|
--color-accent-content: oklch(25% 0.006 285);
|
||||||
|
--color-neutral: oklch(14% 0.005 285.823);
|
||||||
|
--color-neutral-content: oklch(92% 0.004 286.32);
|
||||||
|
--color-info: oklch(74% 0.16 232);
|
||||||
|
--color-info-content: oklch(29% 0.066 243.157);
|
||||||
|
--color-success: oklch(62% 0.17 163);
|
||||||
|
--color-success-content: oklch(37% 0.077 168.94);
|
||||||
|
--color-warning: oklch(82% 0.18 84);
|
||||||
|
--color-warning-content: oklch(41% 0.112 45.904);
|
||||||
|
--color-error: oklch(60% 0.25 27);
|
||||||
|
--color-error-content: oklch(27% 0.105 12.094);
|
||||||
|
--radius-selector: 0.5rem;
|
||||||
|
--radius-field: 0.4rem;
|
||||||
|
--radius-box: 0.5rem;
|
||||||
|
--size-selector: 0.25rem;
|
||||||
|
--size-field: 0.25rem;
|
||||||
|
--border: 1px;
|
||||||
|
--depth: 1;
|
||||||
|
--noise: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer base {
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root:not([data-theme]) {
|
||||||
|
color-scheme: dark;
|
||||||
|
--color-base-100: oklch(15% 0.005 285.823);
|
||||||
|
--color-base-200: oklch(20% 0.005 285.823);
|
||||||
|
--color-base-300: oklch(30% 0.005 285.823);
|
||||||
|
--color-base-content: oklch(92% 0.004 286.32);
|
||||||
|
--color-primary: oklch(98% 0 0);
|
||||||
|
--color-primary-content: oklch(15% 0 0);
|
||||||
|
--color-secondary: oklch(65% 0.046 257.417);
|
||||||
|
--color-secondary-content: oklch(15% 0.005 285.823);
|
||||||
|
--color-accent: oklch(25% 0 0);
|
||||||
|
--color-accent-content: oklch(98% 0 0);
|
||||||
|
--color-neutral: oklch(92% 0.004 286.32);
|
||||||
|
--color-neutral-content: oklch(14% 0.005 285.823);
|
||||||
|
--color-info: oklch(70% 0.1 230);
|
||||||
|
--color-info-content: oklch(29% 0.066 243.157);
|
||||||
|
--color-success: oklch(65% 0.15 160);
|
||||||
|
--color-success-content: oklch(37% 0.077 168.94);
|
||||||
|
--color-warning: oklch(85% 0.15 90);
|
||||||
|
--color-warning-content: oklch(41% 0.112 45.904);
|
||||||
|
--color-error: oklch(55% 0.2 27);
|
||||||
|
--color-error-content: oklch(27% 0.105 12.094);
|
||||||
|
--radius-selector: 0.5rem;
|
||||||
|
--radius-field: 0.4rem;
|
||||||
|
--radius-box: 0.5rem;
|
||||||
|
--size-selector: 0.25rem;
|
||||||
|
--size-field: 0.25rem;
|
||||||
|
--border: 1px;
|
||||||
|
--depth: 1;
|
||||||
|
--noise: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@layer base {
|
||||||
|
:root:has(input.theme-controller[value=dark]:checked),[data-theme="dark"] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--color-base-100: oklch(15% 0.005 285.823);
|
||||||
|
--color-base-200: oklch(20% 0.005 285.823);
|
||||||
|
--color-base-300: oklch(30% 0.005 285.823);
|
||||||
|
--color-base-content: oklch(92% 0.004 286.32);
|
||||||
|
--color-primary: oklch(98% 0 0);
|
||||||
|
--color-primary-content: oklch(15% 0 0);
|
||||||
|
--color-secondary: oklch(65% 0.046 257.417);
|
||||||
|
--color-secondary-content: oklch(15% 0.005 285.823);
|
||||||
|
--color-accent: oklch(25% 0 0);
|
||||||
|
--color-accent-content: oklch(98% 0 0);
|
||||||
|
--color-neutral: oklch(92% 0.004 286.32);
|
||||||
|
--color-neutral-content: oklch(14% 0.005 285.823);
|
||||||
|
--color-info: oklch(70% 0.1 230);
|
||||||
|
--color-info-content: oklch(29% 0.066 243.157);
|
||||||
|
--color-success: oklch(65% 0.15 160);
|
||||||
|
--color-success-content: oklch(37% 0.077 168.94);
|
||||||
|
--color-warning: oklch(85% 0.15 90);
|
||||||
|
--color-warning-content: oklch(41% 0.112 45.904);
|
||||||
|
--color-error: oklch(55% 0.2 27);
|
||||||
|
--color-error-content: oklch(27% 0.105 12.094);
|
||||||
|
--radius-selector: 0.5rem;
|
||||||
|
--radius-field: 0.4rem;
|
||||||
|
--radius-box: 0.5rem;
|
||||||
|
--size-selector: 0.25rem;
|
||||||
|
--size-field: 0.25rem;
|
||||||
|
--border: 1px;
|
||||||
|
--depth: 1;
|
||||||
|
--noise: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@property --tw-translate-x {
|
@property --tw-translate-x {
|
||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
@@ -7442,42 +7518,6 @@
|
|||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
}
|
}
|
||||||
@property --tw-backdrop-blur {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-brightness {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-contrast {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-grayscale {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-hue-rotate {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-invert {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-opacity {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-saturate {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-backdrop-sepia {
|
|
||||||
syntax: "*";
|
|
||||||
inherits: false;
|
|
||||||
}
|
|
||||||
@property --tw-duration {
|
@property --tw-duration {
|
||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
@@ -7547,15 +7587,6 @@
|
|||||||
--tw-drop-shadow-color: initial;
|
--tw-drop-shadow-color: initial;
|
||||||
--tw-drop-shadow-alpha: 100%;
|
--tw-drop-shadow-alpha: 100%;
|
||||||
--tw-drop-shadow-size: initial;
|
--tw-drop-shadow-size: initial;
|
||||||
--tw-backdrop-blur: initial;
|
|
||||||
--tw-backdrop-brightness: initial;
|
|
||||||
--tw-backdrop-contrast: initial;
|
|
||||||
--tw-backdrop-grayscale: initial;
|
|
||||||
--tw-backdrop-hue-rotate: initial;
|
|
||||||
--tw-backdrop-invert: initial;
|
|
||||||
--tw-backdrop-opacity: initial;
|
|
||||||
--tw-backdrop-saturate: initial;
|
|
||||||
--tw-backdrop-sepia: initial;
|
|
||||||
--tw-duration: initial;
|
--tw-duration: initial;
|
||||||
--tw-content: "";
|
--tw-content: "";
|
||||||
}
|
}
|
||||||
|
|||||||
2
css/sigpro.min.css
vendored
2
css/sigpro.min.css
vendored
File diff suppressed because one or more lines are too long
2030
dist/sigpro-ui.esm.js
vendored
Normal file
2030
dist/sigpro-ui.esm.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
dist/sigpro-ui.esm.min.js
vendored
Normal file
7
dist/sigpro-ui.esm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1303
dist/sigpro-ui.js
vendored
1303
dist/sigpro-ui.js
vendored
File diff suppressed because it is too large
Load Diff
8
dist/sigpro-ui.min.js
vendored
8
dist/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -90,7 +90,7 @@ To achieve the performance promised by SigPro-UI, your environment must be equip
|
|||||||
|
|
||||||
### 1. SigPro Core
|
### 1. SigPro Core
|
||||||
|
|
||||||
The atomic heart. SigPro-UI requires the SigPro runtime (`$`, `$watch`, `$html`, etc.) to be present in the global scope or provided as a module.
|
The atomic heart. SigPro-UI requires the SigPro runtime (`$`, `Watch`, `Tag`, etc.) to be present in the global scope or provided as a module.
|
||||||
|
|
||||||
### 2. daisyUI v5
|
### 2. daisyUI v5
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const BasicDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Group Accordion (Radio Style)
|
### Group Accordion (Radio Style)
|
||||||
@@ -112,7 +112,7 @@ const GroupDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(GroupDemo, '#demo-group');
|
Mount(GroupDemo, '#demo-group');
|
||||||
```
|
```
|
||||||
|
|
||||||
### FAQ Accordion
|
### FAQ Accordion
|
||||||
@@ -146,7 +146,7 @@ const FaqDemo = () => {
|
|||||||
])
|
])
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
$mount(FaqDemo, '#demo-faq');
|
Mount(FaqDemo, '#demo-faq');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Rich Content
|
### With Rich Content
|
||||||
@@ -218,7 +218,7 @@ const RichDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(RichDemo, '#demo-rich');
|
Mount(RichDemo, '#demo-rich');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Accordion
|
### Form Accordion
|
||||||
@@ -346,7 +346,7 @@ const FormAccordion = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FormAccordion, '#demo-form');
|
Mount(FormAccordion, '#demo-form');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -396,5 +396,5 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -60,7 +60,7 @@ const BasicDemo = () => {
|
|||||||
Alert({ type: 'error', message: 'An error occurred while processing your request.' })
|
Alert({ type: 'error', message: 'An error occurred while processing your request.' })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Soft vs Solid Variants
|
### Soft vs Solid Variants
|
||||||
@@ -81,7 +81,7 @@ const VariantsDemo = () => {
|
|||||||
Alert({ type: 'success', soft: false, message: 'Solid success alert' })
|
Alert({ type: 'success', soft: false, message: 'Solid success alert' })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Actions
|
### With Actions
|
||||||
@@ -130,7 +130,7 @@ const ActionsDemo = () => {
|
|||||||
}) : null
|
}) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
Mount(ActionsDemo, '#demo-actions');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dismissible Alert
|
### Dismissible Alert
|
||||||
@@ -161,7 +161,7 @@ const DismissibleDemo = () => {
|
|||||||
}, 'Show Alert') : null
|
}, 'Show Alert') : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DismissibleDemo, '#demo-dismissible');
|
Mount(DismissibleDemo, '#demo-dismissible');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Alert
|
### Reactive Alert
|
||||||
@@ -201,7 +201,7 @@ const ReactiveDemo = () => {
|
|||||||
}) : null
|
}) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Validation
|
### Form Validation
|
||||||
@@ -268,7 +268,7 @@ const FormDemo = () => {
|
|||||||
}) : null
|
}) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FormDemo, '#demo-form');
|
Mount(FormDemo, '#demo-form');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Icon Alerts
|
### Icon Alerts
|
||||||
@@ -289,7 +289,7 @@ const IconsDemo = () => {
|
|||||||
Alert({ type: 'error', message: 'Error alert with custom icon' })
|
Alert({ type: 'error', message: 'Error alert with custom icon' })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Types
|
### All Types
|
||||||
@@ -310,5 +310,5 @@ const AllTypesDemo = () => {
|
|||||||
Alert({ type: 'error', message: '❌ This is an error alert' })
|
Alert({ type: 'error', message: '❌ This is an error alert' })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(AllTypesDemo, '#demo-all');
|
Mount(AllTypesDemo, '#demo-all');
|
||||||
```
|
```
|
||||||
@@ -13,7 +13,7 @@ Searchable dropdown with autocomplete functionality, keyboard navigation, and re
|
|||||||
| `class` | `string` | `''` | Additional CSS classes for the container |
|
| `class` | `string` | `''` | Additional CSS classes for the container |
|
||||||
| `items` | `Array<string \| {value: string, label: string}> \| Signal` | `[]` | Items to search from |
|
| `items` | `Array<string \| {value: string, label: string}> \| Signal` | `[]` | Items to search from |
|
||||||
| `value` | `string \| Signal<string>` | `''` | Selected value (reactive) |
|
| `value` | `string \| Signal<string>` | `''` | Selected value (reactive) |
|
||||||
| `onSelect` | `function(item)` | `-` | Called when an option is selected |
|
| `onselect` | `function(item)` | `-` | Called when an option is selected |
|
||||||
| `label` | `string` | `-` | Label text for the input |
|
| `label` | `string` | `-` | Label text for the input |
|
||||||
| `placeholder` | `string` | `'Search...'` | Placeholder text |
|
| `placeholder` | `string` | `'Search...'` | Placeholder text |
|
||||||
|
|
||||||
@@ -48,10 +48,10 @@ const BasicDemo = () => {
|
|||||||
return Autocomplete({
|
return Autocomplete({
|
||||||
items: fruits,
|
items: fruits,
|
||||||
value: selected,
|
value: selected,
|
||||||
onSelect: (value) => selected(value)
|
onselect: (value) => selected(value)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Objects
|
### With Objects
|
||||||
@@ -81,7 +81,7 @@ const ObjectsDemo = () => {
|
|||||||
Autocomplete({
|
Autocomplete({
|
||||||
items: countries,
|
items: countries,
|
||||||
value: selectedLabel,
|
value: selectedLabel,
|
||||||
onSelect: (item) => {
|
onselect: (item) => {
|
||||||
const selectedItem = typeof item === 'string'
|
const selectedItem = typeof item === 'string'
|
||||||
? countries.find(c => c.label === item)
|
? countries.find(c => c.label === item)
|
||||||
: item;
|
: item;
|
||||||
@@ -94,7 +94,7 @@ const ObjectsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ObjectsDemo, '#demo-objects');
|
Mount(ObjectsDemo, '#demo-objects');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Reactive Display
|
### With Reactive Display
|
||||||
@@ -118,14 +118,14 @@ const ReactiveDemo = () => {
|
|||||||
Autocomplete({
|
Autocomplete({
|
||||||
items: programmingLanguages,
|
items: programmingLanguages,
|
||||||
value: selected,
|
value: selected,
|
||||||
onSelect: (value) => selected(value)
|
onselect: (value) => selected(value)
|
||||||
}),
|
}),
|
||||||
() => selected() ? Div({ class: 'alert alert-info' }, [
|
() => selected() ? Div({ class: 'alert alert-info' }, [
|
||||||
`You selected: ${selected()}`
|
`You selected: ${selected()}`
|
||||||
]) : null
|
]) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dynamic Items
|
### Dynamic Items
|
||||||
@@ -161,11 +161,11 @@ const DynamicDemo = () => {
|
|||||||
Autocomplete({
|
Autocomplete({
|
||||||
items: () => allItems[filterType()],
|
items: () => allItems[filterType()],
|
||||||
value: selected,
|
value: selected,
|
||||||
onSelect: (value) => selected(value)
|
onselect: (value) => selected(value)
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DynamicDemo, '#demo-dynamic');
|
Mount(DynamicDemo, '#demo-dynamic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -208,5 +208,5 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -56,7 +56,7 @@ const BasicDemo = () => {
|
|||||||
Badge({ class: 'badge-error' }, 'Error')
|
Badge({ class: 'badge-error' }, 'Error')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Badge Sizes
|
### Badge Sizes
|
||||||
@@ -78,7 +78,7 @@ const SizesDemo = () => {
|
|||||||
Badge({ class: 'badge-lg' }, 'Large')
|
Badge({ class: 'badge-lg' }, 'Large')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(SizesDemo, '#demo-sizes');
|
Mount(SizesDemo, '#demo-sizes');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Outline Badges
|
### Outline Badges
|
||||||
@@ -103,7 +103,7 @@ const OutlineDemo = () => {
|
|||||||
Badge({ class: 'badge-outline badge-error' }, 'Error')
|
Badge({ class: 'badge-outline badge-error' }, 'Error')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(OutlineDemo, '#demo-outline');
|
Mount(OutlineDemo, '#demo-outline');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ghost Badges
|
### Ghost Badges
|
||||||
@@ -128,7 +128,7 @@ const GhostDemo = () => {
|
|||||||
Badge({ class: 'badge-ghost badge-error' }, 'Error')
|
Badge({ class: 'badge-ghost badge-error' }, 'Error')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(GhostDemo, '#demo-ghost');
|
Mount(GhostDemo, '#demo-ghost');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icons
|
### With Icons
|
||||||
@@ -165,7 +165,7 @@ const IconsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Status Badges
|
### Status Badges
|
||||||
@@ -194,7 +194,7 @@ const StatusDemo = () => {
|
|||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(StatusDemo, '#demo-status');
|
Mount(StatusDemo, '#demo-status');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Count Badges
|
### Count Badges
|
||||||
@@ -227,7 +227,7 @@ const CountDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(CountDemo, '#demo-count');
|
Mount(CountDemo, '#demo-count');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interactive Badge
|
### Interactive Badge
|
||||||
@@ -255,7 +255,7 @@ const InteractiveDemo = () => {
|
|||||||
}, 'Reset')
|
}, 'Reset')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
Mount(InteractiveDemo, '#demo-interactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -287,7 +287,7 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Inline with Text
|
### Inline with Text
|
||||||
@@ -319,5 +319,5 @@ const InlineDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(InlineDemo, '#demo-inline');
|
Mount(InlineDemo, '#demo-inline');
|
||||||
```
|
```
|
||||||
@@ -48,7 +48,7 @@ Button({ class: "btn-primary btn-lg btn-circle gap-4"}, "Click Me");
|
|||||||
const BasicDemo = () => {
|
const BasicDemo = () => {
|
||||||
return Button({ class: "btn-primary" }, "Click Me");
|
return Button({ class: "btn-primary" }, "Click Me");
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, "#demo-basic");
|
Mount(BasicDemo, "#demo-basic");
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Loading State
|
### With Loading State
|
||||||
@@ -72,7 +72,7 @@ const LoadingDemo = () => {
|
|||||||
"Save Changes",
|
"Save Changes",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
$mount(LoadingDemo, "#demo-loading");
|
Mount(LoadingDemo, "#demo-loading");
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icon
|
### With Icon
|
||||||
@@ -89,7 +89,7 @@ const IconDemo = () => {
|
|||||||
"Favorite",
|
"Favorite",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
$mount(IconDemo, "#demo-icon");
|
Mount(IconDemo, "#demo-icon");
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Badge (using Indicator)
|
### With Badge (using Indicator)
|
||||||
@@ -103,7 +103,7 @@ const BadgeDemo = () => {
|
|||||||
Button({ class: "btn-outline" }, "Notifications"),
|
Button({ class: "btn-outline" }, "Notifications"),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
$mount(BadgeDemo, "#demo-badge");
|
Mount(BadgeDemo, "#demo-badge");
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Tooltip
|
### With Tooltip
|
||||||
@@ -114,7 +114,7 @@ $mount(BadgeDemo, "#demo-badge");
|
|||||||
const TooltipDemo = () => {
|
const TooltipDemo = () => {
|
||||||
return Tooltip({ tip: "Delete item" }, Button({ class: "btn-ghost" }, "Delete"));
|
return Tooltip({ tip: "Delete item" }, Button({ class: "btn-ghost" }, "Delete"));
|
||||||
};
|
};
|
||||||
$mount(TooltipDemo, "#demo-tooltip");
|
Mount(TooltipDemo, "#demo-tooltip");
|
||||||
```
|
```
|
||||||
|
|
||||||
### Combined (Badge + Tooltip)
|
### Combined (Badge + Tooltip)
|
||||||
@@ -139,7 +139,7 @@ const CombinedDemo = () => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
$mount(CombinedDemo, "#demo-combined");
|
Mount(CombinedDemo, "#demo-combined");
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Color Variants
|
### All Color Variants
|
||||||
@@ -156,5 +156,5 @@ const VariantsDemo = () => {
|
|||||||
Button({ class: "btn-outline" }, "Outline"),
|
Button({ class: "btn-outline" }, "Outline"),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, "#demo-variants");
|
Mount(VariantsDemo, "#demo-variants");
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Checkbox
|
# Checkbox
|
||||||
|
|
||||||
Toggle checkbox component with label, tooltip support, and reactive state management.
|
Toggle checkbox component with label support and reactive state management.
|
||||||
|
|
||||||
## Tag
|
## Tag
|
||||||
|
|
||||||
@@ -12,7 +12,6 @@ Toggle checkbox component with label, tooltip support, and reactive state manage
|
|||||||
| :--- | :--- | :--- | :--- |
|
| :--- | :--- | :--- | :--- |
|
||||||
| `label` | `string` | `-` | Label text for the checkbox |
|
| `label` | `string` | `-` | Label text for the checkbox |
|
||||||
| `value` | `boolean \| Signal<boolean>` | `false` | Checked state |
|
| `value` | `boolean \| Signal<boolean>` | `false` | Checked state |
|
||||||
| `tooltip` | `string` | `-` | Tooltip text on hover |
|
|
||||||
| `toggle` | `boolean` | `false` | Display as toggle switch instead of checkbox |
|
| `toggle` | `boolean` | `false` | Display as toggle switch instead of checkbox |
|
||||||
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||||
@@ -62,7 +61,7 @@ const BasicDemo = () => {
|
|||||||
onclick: () => accepted(!accepted())
|
onclick: () => accepted(!accepted())
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Toggle Switch
|
### Toggle Switch
|
||||||
@@ -90,30 +89,7 @@ const ToggleDemo = () => {
|
|||||||
: Div({ class: 'alert alert-soft' }, 'Notifications are OFF')
|
: Div({ class: 'alert alert-soft' }, 'Notifications are OFF')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ToggleDemo, '#demo-toggle');
|
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
|
### Disabled State
|
||||||
@@ -140,7 +116,7 @@ const DisabledDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DisabledDemo, '#demo-disabled');
|
Mount(DisabledDemo, '#demo-disabled');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Multiple Selection
|
### Reactive Multiple Selection
|
||||||
@@ -193,7 +169,33 @@ const MultipleDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(MultipleDemo, '#demo-multiple');
|
Mount(MultipleDemo, '#demo-multiple');
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const TooltipDemo = () => {
|
||||||
|
const accepted = $(false);
|
||||||
|
|
||||||
|
return Tooltip({
|
||||||
|
tip: "You must accept the terms to continue"
|
||||||
|
},
|
||||||
|
Checkbox({
|
||||||
|
label: 'I accept the terms',
|
||||||
|
value: accepted,
|
||||||
|
onclick: () => accepted(!accepted())
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Mount(TooltipDemo, '#demo-tooltip');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -242,7 +244,7 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Example
|
### Form Example
|
||||||
@@ -286,5 +288,5 @@ const FormDemo = () => {
|
|||||||
: null
|
: null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FormDemo, '#demo-form');
|
Mount(FormDemo, '#demo-form');
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const BasicDemo = () => {
|
|||||||
value: color
|
value: color
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Reactive Preview
|
### With Reactive Preview
|
||||||
@@ -74,7 +74,7 @@ const PreviewDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PreviewDemo, '#demo-preview');
|
Mount(PreviewDemo, '#demo-preview');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Color Palette Grid
|
### Color Palette Grid
|
||||||
@@ -112,7 +112,7 @@ const PaletteDemo = () => {
|
|||||||
Div({ class: 'mt-2 text-center text-sm font-mono' }, () => selectedColor())
|
Div({ class: 'mt-2 text-center text-sm font-mono' }, () => selectedColor())
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PaletteDemo, '#demo-palette');
|
Mount(PaletteDemo, '#demo-palette');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Text Color Preview
|
### With Text Color Preview
|
||||||
@@ -148,7 +148,7 @@ const TextDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(TextDemo, '#demo-text');
|
Mount(TextDemo, '#demo-text');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -181,7 +181,7 @@ const VariantsDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dynamic Color Swatch
|
### Dynamic Color Swatch
|
||||||
@@ -221,5 +221,5 @@ const DynamicDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DynamicDemo, '#demo-dynamic');
|
Mount(DynamicDemo, '#demo-dynamic');
|
||||||
```
|
```
|
||||||
@@ -50,7 +50,7 @@ const BasicDemo = () => {
|
|||||||
placeholder: 'Choose a date...'
|
placeholder: 'Choose a date...'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Date Range Picker
|
### Date Range Picker
|
||||||
@@ -78,7 +78,7 @@ const RangeDemo = () => {
|
|||||||
]) : null
|
]) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(RangeDemo, '#demo-range');
|
Mount(RangeDemo, '#demo-range');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Time Selection
|
### With Time Selection
|
||||||
@@ -106,7 +106,7 @@ const TimeDemo = () => {
|
|||||||
]) : null
|
]) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(TimeDemo, '#demo-time');
|
Mount(TimeDemo, '#demo-time');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Range with Time
|
### Range with Time
|
||||||
@@ -135,7 +135,7 @@ const RangeTimeDemo = () => {
|
|||||||
]) : null
|
]) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(RangeTimeDemo, '#demo-range-time');
|
Mount(RangeTimeDemo, '#demo-range-time');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Display
|
### Reactive Display
|
||||||
@@ -167,7 +167,7 @@ const ReactiveDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -201,5 +201,5 @@ const VariantsDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -61,7 +61,7 @@ const BasicDemo = () => {
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Navigation Drawer
|
### Navigation Drawer
|
||||||
@@ -138,7 +138,7 @@ const NavDrawer = () => {
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(NavDrawer, '#demo-nav');
|
Mount(NavDrawer, '#demo-nav');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Settings Drawer
|
### Settings Drawer
|
||||||
@@ -233,7 +233,7 @@ const SettingsDrawer = () => {
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(SettingsDrawer, '#demo-settings');
|
Mount(SettingsDrawer, '#demo-settings');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cart Drawer
|
### Cart Drawer
|
||||||
@@ -336,7 +336,7 @@ const CartDrawer = () => {
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(CartDrawer, '#demo-cart');
|
Mount(CartDrawer, '#demo-cart');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Responsive Drawer
|
### Responsive Drawer
|
||||||
@@ -404,7 +404,7 @@ const ResponsiveDrawer = () => {
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(ResponsiveDrawer, '#demo-responsive');
|
Mount(ResponsiveDrawer, '#demo-responsive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Drawer
|
### Form Drawer
|
||||||
@@ -462,7 +462,7 @@ const FormDrawer = () => {
|
|||||||
}),
|
}),
|
||||||
Div({ class: 'form-control' }, [
|
Div({ class: 'form-control' }, [
|
||||||
Span({ class: 'label-text mb-1' }, 'Message'),
|
Span({ class: 'label-text mb-1' }, 'Message'),
|
||||||
$html('textarea', {
|
Tag('textarea', {
|
||||||
class: 'textarea textarea-bordered h-24',
|
class: 'textarea textarea-bordered h-24',
|
||||||
placeholder: 'Your message',
|
placeholder: 'Your message',
|
||||||
value: message,
|
value: message,
|
||||||
@@ -489,5 +489,5 @@ const FormDrawer = () => {
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(FormDrawer, '#demo-form');
|
Mount(FormDrawer, '#demo-form');
|
||||||
```
|
```
|
||||||
@@ -71,7 +71,7 @@ const BasicDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icons
|
### With Icons
|
||||||
@@ -96,7 +96,7 @@ const IconsDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Action Dropdown
|
### Action Dropdown
|
||||||
@@ -124,7 +124,7 @@ const ActionsDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
Mount(ActionsDemo, '#demo-actions');
|
||||||
```
|
```
|
||||||
|
|
||||||
### User Dropdown
|
### User Dropdown
|
||||||
@@ -153,7 +153,7 @@ const UserDropdown = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(UserDropdown, '#demo-user');
|
Mount(UserDropdown, '#demo-user');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Items
|
### Reactive Items
|
||||||
@@ -180,7 +180,7 @@ const ReactiveDropdown = () => {
|
|||||||
items: items
|
items: items
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(ReactiveDropdown, '#demo-reactive');
|
Mount(ReactiveDropdown, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Notification Dropdown
|
### Notification Dropdown
|
||||||
@@ -225,7 +225,7 @@ const NotificationsDropdown = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(NotificationsDropdown, '#demo-notifications');
|
Mount(NotificationsDropdown, '#demo-notifications');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -251,5 +251,5 @@ const VariantsDemo = () => {
|
|||||||
Dropdown({ label: 'End Position', class: 'dropdown-end', items: commonItems })
|
Dropdown({ label: 'End Position', class: 'dropdown-end', items: commonItems })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -75,7 +75,7 @@ const BasicDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Label
|
### With Label
|
||||||
@@ -101,7 +101,7 @@ const LabelDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(LabelDemo, '#demo-label');
|
Mount(LabelDemo, '#demo-label');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Different Positions
|
### Different Positions
|
||||||
@@ -145,7 +145,7 @@ const PositionsDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PositionsDemo, '#demo-positions');
|
Mount(PositionsDemo, '#demo-positions');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Color Variants
|
### Color Variants
|
||||||
@@ -190,7 +190,7 @@ const ColorsDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ColorsDemo, '#demo-colors');
|
Mount(ColorsDemo, '#demo-colors');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Actions
|
### Reactive Actions
|
||||||
@@ -236,7 +236,7 @@ const ReactiveActions = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveActions, '#demo-reactive');
|
Mount(ReactiveActions, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Document Actions
|
### Document Actions
|
||||||
@@ -275,7 +275,7 @@ const DocumentActions = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DocumentActions, '#demo-document');
|
Mount(DocumentActions, '#demo-document');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Messaging FAB
|
### Messaging FAB
|
||||||
@@ -333,7 +333,7 @@ const MessagingFAB = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(MessagingFAB, '#demo-messaging');
|
Mount(MessagingFAB, '#demo-messaging');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -374,5 +374,5 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -62,7 +62,7 @@ const BasicDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Reactive Legend
|
### With Reactive Legend
|
||||||
@@ -104,7 +104,7 @@ const ReactiveDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Address Form
|
### Address Form
|
||||||
@@ -146,7 +146,7 @@ const AddressDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(AddressDemo, '#demo-address');
|
Mount(AddressDemo, '#demo-address');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Payment Method
|
### Payment Method
|
||||||
@@ -187,7 +187,7 @@ const PaymentDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PaymentDemo, '#demo-payment');
|
Mount(PaymentDemo, '#demo-payment');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Preferences Panel
|
### Preferences Panel
|
||||||
@@ -236,7 +236,7 @@ const PreferencesDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PreferencesDemo, '#demo-preferences');
|
Mount(PreferencesDemo, '#demo-preferences');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Registration Form
|
### Registration Form
|
||||||
@@ -294,7 +294,7 @@ const RegistrationDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(RegistrationDemo, '#demo-registration');
|
Mount(RegistrationDemo, '#demo-registration');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -320,5 +320,5 @@ const VariantsDemo = () => {
|
|||||||
Fieldset({ legend: 'With Background', class: 'w-full bg-base-100' }, [commonContent])
|
Fieldset({ legend: 'With Background', class: 'w-full bg-base-100' }, [commonContent])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -59,7 +59,7 @@ const BasicDemo = () => {
|
|||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Online Status Indicator
|
### Online Status Indicator
|
||||||
@@ -91,7 +91,7 @@ const StatusDemo = () => {
|
|||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(StatusDemo, '#demo-status');
|
Mount(StatusDemo, '#demo-status');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Counter
|
### Reactive Counter
|
||||||
@@ -129,7 +129,7 @@ const ReactiveDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Shopping Cart
|
### Shopping Cart
|
||||||
@@ -188,7 +188,7 @@ const CartDemo = () => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(CartDemo, '#demo-cart');
|
Mount(CartDemo, '#demo-cart');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Email Inbox
|
### Email Inbox
|
||||||
@@ -249,7 +249,7 @@ const InboxDemo = () => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(InboxDemo, '#demo-inbox');
|
Mount(InboxDemo, '#demo-inbox');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -280,5 +280,5 @@ const VariantsDemo = () => {
|
|||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
# Input
|
# Input
|
||||||
|
|
||||||
Form input component with floating label, icons, password toggle, tooltip, and error states. Fully integrated with DaisyUI and Tailwind.
|
Form input component with icons, password toggle, and validation. Use `Label()` and `Tooltip()` as wrappers for label and tooltip functionality.
|
||||||
|
|
||||||
|
|
||||||
## Tag
|
## Tag
|
||||||
|
|
||||||
@@ -11,16 +10,24 @@ Form input component with floating label, icons, password toggle, tooltip, and e
|
|||||||
|
|
||||||
| Prop | Type | Default | Description |
|
| Prop | Type | Default | Description |
|
||||||
| :----------- | :--------------------------- | :--------- | :----------------------------------------------- |
|
| :----------- | :--------------------------- | :--------- | :----------------------------------------------- |
|
||||||
| `label` | `string` | `-` | Label text (floating style) |
|
|
||||||
| `type` | `string` | `'text'` | Input type (text, password, email, number, date) |
|
| `type` | `string` | `'text'` | Input type (text, password, email, number, date) |
|
||||||
| `value` | `string \| Signal<string>` | `''` | Input value |
|
| `value` | `string \| Signal<string>` | `''` | Input value |
|
||||||
| `placeholder`| `string` | `' '` | Placeholder text |
|
| `placeholder`| `string` | `' '` | Placeholder text |
|
||||||
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed inside input |
|
| `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 |
|
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||||
| `oninput` | `function` | `-` | Input event handler |
|
| `oninput` | `function` | `-` | Input event handler |
|
||||||
|
| `validate` | `function` | `-` | Validation function returning error message |
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
Input supports all **daisyUI Input classes**:
|
||||||
|
|
||||||
|
| Category | Keywords | Description |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| Style | `input-bordered`, `input-ghost` | Input style variants |
|
||||||
|
| Color | `input-primary`, `input-secondary`, `input-accent`, `input-info`, `input-success`, `input-warning`, `input-error` | Input color variants |
|
||||||
|
| Size | `input-xs`, `input-sm`, `input-md`, `input-lg` | Input size variants |
|
||||||
|
|
||||||
## Live Examples
|
## Live Examples
|
||||||
|
|
||||||
@@ -43,7 +50,7 @@ const BasicDemo = () => {
|
|||||||
oninput: (e) => name(e.target.value)
|
oninput: (e) => name(e.target.value)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icon
|
### With Icon
|
||||||
@@ -66,7 +73,7 @@ const IconDemo = () => {
|
|||||||
oninput: (e) => email(e.target.value)
|
oninput: (e) => email(e.target.value)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(IconDemo, '#demo-icon');
|
Mount(IconDemo, '#demo-icon');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Password with Toggle
|
### Password with Toggle
|
||||||
@@ -88,10 +95,39 @@ const PasswordDemo = () => {
|
|||||||
oninput: (e) => password(e.target.value)
|
oninput: (e) => password(e.target.value)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(PasswordDemo, '#demo-password');
|
Mount(PasswordDemo, '#demo-password');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Tooltip
|
### With Floating Label
|
||||||
|
|
||||||
|
Wrap the input with `Label()` component:
|
||||||
|
|
||||||
|
<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-label" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const LabelDemo = () => {
|
||||||
|
const email = $('');
|
||||||
|
|
||||||
|
return Input({
|
||||||
|
type: 'email',
|
||||||
|
label: "Email",
|
||||||
|
floating: true,
|
||||||
|
value: email,
|
||||||
|
placeholder: ' ',
|
||||||
|
oninput: (e) => email(e.target.value)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Mount(LabelDemo, '#demo-label');
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Tooltip & label
|
||||||
|
|
||||||
|
Wrap the input with `Tooltip()` component:
|
||||||
|
|
||||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -104,13 +140,16 @@ $mount(PasswordDemo, '#demo-password');
|
|||||||
const TooltipDemo = () => {
|
const TooltipDemo = () => {
|
||||||
const username = $('');
|
const username = $('');
|
||||||
|
|
||||||
return Input({
|
return Tooltip({ tip: 'Must be at least 3 characters' },
|
||||||
tip: 'Must be at least 3 characters',
|
Input({
|
||||||
value: username,
|
value: username,
|
||||||
oninput: (e) => username(e.target.value)
|
label: "Username",
|
||||||
});
|
placeholder: 'Username',
|
||||||
|
oninput: (e) => username(e.target.value)
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
$mount(TooltipDemo, '#demo-tooltip');
|
Mount(TooltipDemo, '#demo-tooltip');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Error State
|
### Error State
|
||||||
@@ -126,11 +165,12 @@ $mount(TooltipDemo, '#demo-tooltip');
|
|||||||
const ErrorDemo = () => {
|
const ErrorDemo = () => {
|
||||||
const email = $('');
|
const email = $('');
|
||||||
|
|
||||||
return Div({ class: 'w-full max-w-md' }, [
|
return Div({ class: 'w-full' }, [
|
||||||
Input({
|
Input({
|
||||||
type: 'email',
|
type: 'email',
|
||||||
value: email,
|
value: email,
|
||||||
placeholder: 'Enter your email',
|
placeholder: 'Enter your email',
|
||||||
|
label: 'Email',
|
||||||
icon: 'icon-[lucide--mail]',
|
icon: 'icon-[lucide--mail]',
|
||||||
validate: (value) => {
|
validate: (value) => {
|
||||||
if (!value) return '';
|
if (!value) return '';
|
||||||
@@ -142,7 +182,7 @@ const ErrorDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ErrorDemo, '#demo-error');
|
Mount(ErrorDemo, '#demo-error');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Disabled State
|
### Disabled State
|
||||||
@@ -161,7 +201,7 @@ const DisabledDemo = () => {
|
|||||||
disabled: true
|
disabled: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(DisabledDemo, '#demo-disabled');
|
Mount(DisabledDemo, '#demo-disabled');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -193,17 +233,15 @@ const VariantsDemo = () => {
|
|||||||
type: 'date',
|
type: 'date',
|
||||||
value: $('2024-01-01')
|
value: $('2024-01-01')
|
||||||
}),
|
}),
|
||||||
Input({class: 'input-primary',value:"Primary"}),
|
Input({class: 'input-primary', value: "Primary"}),
|
||||||
Input({class: 'input-secondary', value:"Secondary"}),
|
Input({class: 'input-secondary', value: "Secondary"}),
|
||||||
Input({class: 'input-accent', value:"Accent"}),
|
Input({class: 'input-accent', value: "Accent"}),
|
||||||
Input({class: 'input-ghost', value:"Ghost"}),
|
Input({class: 'input-ghost', value: "Ghost"}),
|
||||||
Input({class: 'input-link', value:"Link"}),
|
Input({class: 'input-info', value: "Info"}),
|
||||||
Input({class: 'input-info', value:"Info"}),
|
Input({class: 'input-success', value: "Success"}),
|
||||||
Input({class: 'input-success', value:"Success"}),
|
Input({class: 'input-warning', value: "Warning"}),
|
||||||
Input({class: 'input-warning', value:"Warning"}),
|
Input({class: 'input-error', value: "Error"}),
|
||||||
Input({class: 'input-error', value:"Error"}),
|
|
||||||
|
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -51,7 +51,7 @@ const BasicDemo = () => {
|
|||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, "#demo-basic");
|
Mount(BasicDemo, "#demo-basic");
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Header
|
### With Header
|
||||||
@@ -90,7 +90,7 @@ const HeaderDemo = () => {
|
|||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(HeaderDemo, "#demo-header");
|
Mount(HeaderDemo, "#demo-header");
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icons
|
### With Icons
|
||||||
@@ -134,7 +134,7 @@ const IconsDemo = () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, "#demo-icons");
|
Mount(IconsDemo, "#demo-icons");
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Badges
|
### With Badges
|
||||||
@@ -194,7 +194,7 @@ const BadgesDemo = () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BadgesDemo, "#demo-badges");
|
Mount(BadgesDemo, "#demo-badges");
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interactive List
|
### Interactive List
|
||||||
@@ -252,7 +252,7 @@ const InteractiveDemo = () => {
|
|||||||
: Div({ class: "alert alert-soft" }, "Select a project to see details"),
|
: Div({ class: "alert alert-soft" }, "Select a project to see details"),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(InteractiveDemo, "#demo-interactive");
|
Mount(InteractiveDemo, "#demo-interactive");
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive List (Todo App)
|
### Reactive List (Todo App)
|
||||||
@@ -291,7 +291,7 @@ const ReactiveDemo = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pendingCount = () => todos().filter(t => !t.done).length;
|
const pendingCount = () => todos().filter(t => !t.done).length;
|
||||||
$watch(()=> console.log(pendingCount()));
|
Watch(()=> console.log(pendingCount()));
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
return Div({ class: 'flex flex-col gap-4' }, [
|
||||||
Div({ class: 'flex gap-2' }, [
|
Div({ class: 'flex gap-2' }, [
|
||||||
Input({
|
Input({
|
||||||
@@ -330,7 +330,7 @@ const ReactiveDemo = () => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Avatar List
|
### Avatar List
|
||||||
@@ -383,7 +383,7 @@ const AvatarDemo = () => {
|
|||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(AvatarDemo, "#demo-avatar");
|
Mount(AvatarDemo, "#demo-avatar");
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -421,5 +421,5 @@ const VariantsDemo = () => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, "#demo-variants");
|
Mount(VariantsDemo, "#demo-variants");
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ const BasicDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icons
|
### With Icons
|
||||||
@@ -117,7 +117,7 @@ const IconsDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Nested Menu
|
### Nested Menu
|
||||||
@@ -187,7 +187,7 @@ const NestedDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(NestedDemo, '#demo-nested');
|
Mount(NestedDemo, '#demo-nested');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Horizontal Menu
|
### Horizontal Menu
|
||||||
@@ -232,7 +232,7 @@ const HorizontalDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(HorizontalDemo, '#demo-horizontal');
|
Mount(HorizontalDemo, '#demo-horizontal');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sidebar Menu
|
### Sidebar Menu
|
||||||
@@ -295,7 +295,7 @@ const SidebarDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(SidebarDemo, '#demo-sidebar');
|
Mount(SidebarDemo, '#demo-sidebar');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Account Menu
|
### Account Menu
|
||||||
@@ -346,7 +346,7 @@ const AccountDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(AccountDemo, '#demo-account');
|
Mount(AccountDemo, '#demo-account');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Collapsible Sidebar
|
### Collapsible Sidebar
|
||||||
@@ -384,7 +384,7 @@ const CollapsibleDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(CollapsibleDemo, '#demo-collapsible');
|
Mount(CollapsibleDemo, '#demo-collapsible');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -422,5 +422,5 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -63,9 +63,26 @@ const BasicDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Open the modal using ID.showModal() method -->
|
||||||
|
<button class="btn" onclick="my_modal_1.showModal()">TEST DIRECTO</button>
|
||||||
|
<dialog id="my_modal_1" class="modal">
|
||||||
|
<div class="modal-box">
|
||||||
|
<h3 class="text-lg font-bold">Hello!</h3>
|
||||||
|
<p class="py-4">Press ESC key or click the button below to close</p>
|
||||||
|
<div class="modal-action">
|
||||||
|
<form method="dialog">
|
||||||
|
<!-- if there is a button in form, it will close the modal -->
|
||||||
|
<button class="btn">Close</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
### Modal with Actions
|
### Modal with Actions
|
||||||
|
|
||||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||||
@@ -113,7 +130,7 @@ const ActionsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
Mount(ActionsDemo, '#demo-actions');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Modal
|
### Form Modal
|
||||||
@@ -182,7 +199,7 @@ const FormModal = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FormModal, '#demo-form');
|
Mount(FormModal, '#demo-form');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Confirmation Modal
|
### Confirmation Modal
|
||||||
@@ -243,7 +260,7 @@ const ConfirmDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ConfirmDemo, '#demo-confirm');
|
Mount(ConfirmDemo, '#demo-confirm');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Large Content Modal
|
### Large Content Modal
|
||||||
@@ -295,7 +312,7 @@ const LargeDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(LargeDemo, '#demo-large');
|
Mount(LargeDemo, '#demo-large');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multiple Modals
|
### Multiple Modals
|
||||||
@@ -337,7 +354,7 @@ const MultipleDemo = () => {
|
|||||||
}, 'Please review your input before proceeding.')
|
}, 'Please review your input before proceeding.')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(MultipleDemo, '#demo-multiple');
|
Mount(MultipleDemo, '#demo-multiple');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Styled Modal
|
### Custom Styled Modal
|
||||||
@@ -374,5 +391,5 @@ const CustomDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(CustomDemo, '#demo-custom');
|
Mount(CustomDemo, '#demo-custom');
|
||||||
```
|
```
|
||||||
@@ -51,7 +51,7 @@ const BasicDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Navigation Links
|
### With Navigation Links
|
||||||
@@ -92,7 +92,7 @@ const LinksDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(LinksDemo, '#demo-links');
|
Mount(LinksDemo, '#demo-links');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Search
|
### With Search
|
||||||
@@ -128,7 +128,7 @@ const SearchDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(SearchDemo, '#demo-search');
|
Mount(SearchDemo, '#demo-search');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Avatar and Dropdown
|
### With Avatar and Dropdown
|
||||||
@@ -167,7 +167,7 @@ const AvatarDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(AvatarDemo, '#demo-avatar');
|
Mount(AvatarDemo, '#demo-avatar');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Responsive Navbar
|
### Responsive Navbar
|
||||||
@@ -206,7 +206,7 @@ const ResponsiveDemo = () => {
|
|||||||
]) : null
|
]) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ResponsiveDemo, '#demo-responsive');
|
Mount(ResponsiveDemo, '#demo-responsive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Brand and Actions
|
### With Brand and Actions
|
||||||
@@ -237,7 +237,7 @@ const BrandDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BrandDemo, '#demo-brand');
|
Mount(BrandDemo, '#demo-brand');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -272,5 +272,5 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -70,7 +70,7 @@ const BasicDemo = () => {
|
|||||||
Div({ class: 'mt-2 text-sm opacity-70' }, () => `Selected: ${selected()}`)
|
Div({ class: 'mt-2 text-sm opacity-70' }, () => `Selected: ${selected()}`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Tooltip
|
### With Tooltip
|
||||||
@@ -105,7 +105,7 @@ const TooltipDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(TooltipDemo, '#demo-tooltip');
|
Mount(TooltipDemo, '#demo-tooltip');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Disabled State
|
### Disabled State
|
||||||
@@ -139,7 +139,7 @@ const DisabledDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DisabledDemo, '#demo-disabled');
|
Mount(DisabledDemo, '#demo-disabled');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Preview
|
### Reactive Preview
|
||||||
@@ -199,7 +199,7 @@ const ReactiveDemo = () => {
|
|||||||
}, () => `${size()} ${color()} preview`)
|
}, () => `${size()} ${color()} preview`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Payment Method Selection
|
### Payment Method Selection
|
||||||
@@ -250,7 +250,7 @@ const PaymentDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PaymentDemo, '#demo-payment');
|
Mount(PaymentDemo, '#demo-payment');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -334,7 +334,7 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dynamic Options
|
### Dynamic Options
|
||||||
@@ -405,5 +405,5 @@ const DynamicDemo = () => {
|
|||||||
: null
|
: null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DynamicDemo, '#demo-dynamic');
|
Mount(DynamicDemo, '#demo-dynamic');
|
||||||
```
|
```
|
||||||
@@ -57,7 +57,7 @@ const BasicDemo = () => {
|
|||||||
Div({ class: 'text-center' }, () => `Value: ${value()}%`)
|
Div({ class: 'text-center' }, () => `Value: ${value()}%`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Tooltip
|
### With Tooltip
|
||||||
@@ -85,7 +85,7 @@ const TooltipDemo = () => {
|
|||||||
Div({ class: 'w-full h-20 rounded-lg transition-all', style: () => `background-color: hsl(0, 0%, ${brightness()}%)` })
|
Div({ class: 'w-full h-20 rounded-lg transition-all', style: () => `background-color: hsl(0, 0%, ${brightness()}%)` })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(TooltipDemo, '#demo-tooltip');
|
Mount(TooltipDemo, '#demo-tooltip');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Color Variants
|
### Color Variants
|
||||||
@@ -109,7 +109,7 @@ const ColorsDemo = () => {
|
|||||||
Range({ label: 'Accent', value: accent, class: 'range-accent', oninput: (e) => accent(e.target.value) })
|
Range({ label: 'Accent', value: accent, class: 'range-accent', oninput: (e) => accent(e.target.value) })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ColorsDemo, '#demo-colors');
|
Mount(ColorsDemo, '#demo-colors');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Size Variants
|
### Size Variants
|
||||||
@@ -135,7 +135,7 @@ const SizesDemo = () => {
|
|||||||
Range({ label: 'Large', value: lg, class: 'range-lg', oninput: (e) => lg(e.target.value) })
|
Range({ label: 'Large', value: lg, class: 'range-lg', oninput: (e) => lg(e.target.value) })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(SizesDemo, '#demo-sizes');
|
Mount(SizesDemo, '#demo-sizes');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Price Range
|
### Price Range
|
||||||
@@ -173,7 +173,7 @@ const PriceDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PriceDemo, '#demo-price');
|
Mount(PriceDemo, '#demo-price');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Audio Controls
|
### Audio Controls
|
||||||
@@ -227,7 +227,7 @@ const AudioDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(AudioDemo, '#demo-audio');
|
Mount(AudioDemo, '#demo-audio');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -254,5 +254,5 @@ const VariantsDemo = () => {
|
|||||||
Range({ disabled: true, value: $(50), oninput: (e) => {} })
|
Range({ disabled: true, value: $(50), oninput: (e) => {} })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -54,7 +54,7 @@ const BasicDemo = () => {
|
|||||||
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / 5`)
|
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / 5`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Heart Rating
|
### Heart Rating
|
||||||
@@ -80,7 +80,7 @@ const HeartDemo = () => {
|
|||||||
Div({ class: 'text-sm opacity-70' }, () => `${rating()} hearts`)
|
Div({ class: 'text-sm opacity-70' }, () => `${rating()} hearts`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(HeartDemo, '#demo-heart');
|
Mount(HeartDemo, '#demo-heart');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Star with Outline
|
### Star with Outline
|
||||||
@@ -106,7 +106,7 @@ const Star2Demo = () => {
|
|||||||
Div({ class: 'text-sm opacity-70' }, () => `${rating()} stars`)
|
Div({ class: 'text-sm opacity-70' }, () => `${rating()} stars`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(Star2Demo, '#demo-star2');
|
Mount(Star2Demo, '#demo-star2');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Read-only Rating
|
### Read-only Rating
|
||||||
@@ -131,7 +131,7 @@ const ReadonlyDemo = () => {
|
|||||||
Div({ class: 'text-sm opacity-70' }, 'Average rating: 4.5/5 (read-only)')
|
Div({ class: 'text-sm opacity-70' }, 'Average rating: 4.5/5 (read-only)')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReadonlyDemo, '#demo-readonly');
|
Mount(ReadonlyDemo, '#demo-readonly');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Product Review
|
### Product Review
|
||||||
@@ -186,7 +186,7 @@ const ReviewDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReviewDemo, '#demo-review');
|
Mount(ReviewDemo, '#demo-review');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -219,7 +219,7 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interactive Feedback
|
### Interactive Feedback
|
||||||
@@ -268,5 +268,5 @@ const FeedbackDemo = () => {
|
|||||||
: null
|
: null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FeedbackDemo, '#demo-feedback');
|
Mount(FeedbackDemo, '#demo-feedback');
|
||||||
```
|
```
|
||||||
@@ -56,7 +56,7 @@ const BasicDemo = () => {
|
|||||||
onchange: (e) => selected(e.target.value)
|
onchange: (e) => selected(e.target.value)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Reactive Display
|
### With Reactive Display
|
||||||
@@ -85,7 +85,7 @@ const ReactiveDemo = () => {
|
|||||||
Div({ class: 'alert alert-info' }, () => `You selected: ${selected()}`)
|
Div({ class: 'alert alert-info' }, () => `You selected: ${selected()}`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Disabled State
|
### Disabled State
|
||||||
@@ -110,7 +110,7 @@ const DisabledDemo = () => {
|
|||||||
disabled: true
|
disabled: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(DisabledDemo, '#demo-disabled');
|
Mount(DisabledDemo, '#demo-disabled');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dynamic Items
|
### Dynamic Items
|
||||||
@@ -160,7 +160,7 @@ const DynamicDemo = () => {
|
|||||||
() => selectedItem() ? Div({ class: 'alert alert-success' }, `Selected: ${selectedItem()}`) : null
|
() => selectedItem() ? Div({ class: 'alert alert-success' }, `Selected: ${selectedItem()}`) : null
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DynamicDemo, '#demo-dynamic');
|
Mount(DynamicDemo, '#demo-dynamic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -213,5 +213,5 @@ const VariantsDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -44,7 +44,7 @@ const BasicDemo = () => {
|
|||||||
Div({ class: 'bg-accent text-accent-content rounded-lg p-4 shadow-lg' }, 'Layer 3')
|
Div({ class: 'bg-accent text-accent-content rounded-lg p-4 shadow-lg' }, 'Layer 3')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Card Stack
|
### Card Stack
|
||||||
@@ -73,7 +73,7 @@ const CardsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(CardsDemo, '#demo-cards');
|
Mount(CardsDemo, '#demo-cards');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Avatar Stack
|
### Avatar Stack
|
||||||
@@ -99,7 +99,7 @@ const AvatarsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(AvatarsDemo, '#demo-avatars');
|
Mount(AvatarsDemo, '#demo-avatars');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Image Stack
|
### Image Stack
|
||||||
@@ -125,7 +125,7 @@ const ImagesDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ImagesDemo, '#demo-images');
|
Mount(ImagesDemo, '#demo-images');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Photo Gallery Stack
|
### Photo Gallery Stack
|
||||||
@@ -157,7 +157,7 @@ const GalleryDemo = () => {
|
|||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(GalleryDemo, '#demo-gallery');
|
Mount(GalleryDemo, '#demo-gallery');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interactive Stack
|
### Interactive Stack
|
||||||
@@ -197,7 +197,7 @@ const InteractiveDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
Mount(InteractiveDemo, '#demo-interactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Notification Stack
|
### Notification Stack
|
||||||
@@ -249,7 +249,7 @@ const NotificationsDemo = () => {
|
|||||||
}, 'Clear All')
|
}, 'Clear All')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(NotificationsDemo, '#demo-notifications');
|
Mount(NotificationsDemo, '#demo-notifications');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -290,5 +290,5 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -59,7 +59,7 @@ const BasicDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icons
|
### With Icons
|
||||||
@@ -78,23 +78,23 @@ const IconsDemo = () => {
|
|||||||
label: 'Active Users',
|
label: 'Active Users',
|
||||||
value: '1,234',
|
value: '1,234',
|
||||||
desc: 'Currently online',
|
desc: 'Currently online',
|
||||||
icon: Span({ class: 'text-2xl' }, '👥')
|
icon: Span({ class: 'text-4xl' }, '👥') // text-4xl para emojis
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'New Orders',
|
label: 'New Orders',
|
||||||
value: '89',
|
value: '89',
|
||||||
desc: 'Today',
|
desc: 'Today',
|
||||||
icon: Span({ class: 'text-2xl' }, '📦')
|
icon: Span({ class: 'text-4xl' }, '📦')
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Pending Tasks',
|
label: 'Pending Tasks',
|
||||||
value: '23',
|
value: '23',
|
||||||
desc: 'Need attention',
|
desc: 'Need attention',
|
||||||
icon: Span({ class: 'text-2xl' }, '⏳')
|
icon: Span({ class: 'text-4xl' }, '⏳')
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Values
|
### Reactive Values
|
||||||
@@ -137,7 +137,7 @@ const ReactiveDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multiple Stats in Row
|
### Multiple Stats in Row
|
||||||
@@ -156,29 +156,29 @@ const MultipleDemo = () => {
|
|||||||
label: 'Posts',
|
label: 'Posts',
|
||||||
value: '1,234',
|
value: '1,234',
|
||||||
desc: 'Total content',
|
desc: 'Total content',
|
||||||
icon: Span({ class: 'text-2xl' }, '📝')
|
icon: Span({ class: 'text-4xl' }, '📝') // text-4xl
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Comments',
|
label: 'Comments',
|
||||||
value: '8,901',
|
value: '8,901',
|
||||||
desc: 'Engagement',
|
desc: 'Engagement',
|
||||||
icon: Span({ class: 'text-2xl' }, '💬')
|
icon: Span({ class: 'text-4xl' }, '💬')
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Likes',
|
label: 'Likes',
|
||||||
value: '12,345',
|
value: '12,345',
|
||||||
desc: 'Reactions',
|
desc: 'Reactions',
|
||||||
icon: Span({ class: 'text-2xl' }, '❤️')
|
icon: Span({ class: 'text-4xl' }, '❤️')
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Shares',
|
label: 'Shares',
|
||||||
value: '456',
|
value: '456',
|
||||||
desc: 'Viral reach',
|
desc: 'Viral reach',
|
||||||
icon: Span({ class: 'text-2xl' }, '🔄')
|
icon: Span({ class: 'text-4xl' }, '🔄')
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(MultipleDemo, '#demo-multiple');
|
Mount(MultipleDemo, '#demo-multiple');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dashboard Example
|
### Dashboard Example
|
||||||
@@ -214,25 +214,25 @@ const DashboardDemo = () => {
|
|||||||
label: 'Total Users',
|
label: 'Total Users',
|
||||||
value: () => stats().users.toLocaleString(),
|
value: () => stats().users.toLocaleString(),
|
||||||
desc: 'Registered users',
|
desc: 'Registered users',
|
||||||
icon: Span({ class: 'text-2xl' }, '👥')
|
icon: Span({ class: 'text-4xl' }, '👥') // text-4xl
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Revenue',
|
label: 'Revenue',
|
||||||
value: () => `$${stats().revenue.toLocaleString()}`,
|
value: () => `$${stats().revenue.toLocaleString()}`,
|
||||||
desc: 'This month',
|
desc: 'This month',
|
||||||
icon: Span({ class: 'text-2xl' }, '💰')
|
icon: Span({ class: 'text-4xl' }, '💰')
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Orders',
|
label: 'Orders',
|
||||||
value: () => stats().orders.toLocaleString(),
|
value: () => stats().orders.toLocaleString(),
|
||||||
desc: 'Completed',
|
desc: 'Completed',
|
||||||
icon: Span({ class: 'text-2xl' }, '📦')
|
icon: Span({ class: 'text-4xl' }, '📦')
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Satisfaction',
|
label: 'Satisfaction',
|
||||||
value: () => `${stats().satisfaction}%`,
|
value: () => `${stats().satisfaction}%`,
|
||||||
desc: stats().satisfaction > 90 ? 'Excellent!' : 'Good',
|
desc: stats().satisfaction > 90 ? 'Excellent!' : 'Good',
|
||||||
icon: Span({ class: 'text-2xl' }, '😊')
|
icon: Span({ class: 'text-4xl' }, '😊')
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
Div({ class: 'flex justify-center' }, [
|
Div({ class: 'flex justify-center' }, [
|
||||||
@@ -243,7 +243,7 @@ const DashboardDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DashboardDemo, '#demo-dashboard');
|
Mount(DashboardDemo, '#demo-dashboard');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -262,33 +262,33 @@ const VariantsDemo = () => {
|
|||||||
label: 'Primary Stat',
|
label: 'Primary Stat',
|
||||||
value: '1,234',
|
value: '1,234',
|
||||||
desc: 'With description',
|
desc: 'With description',
|
||||||
icon: Span({ class: 'text-2xl' }, '⭐'),
|
icon: Span({ class: 'text-4xl' }, '⭐'), // text-4xl
|
||||||
class: 'bg-primary/10 text-primary'
|
class: 'bg-primary/10 text-primary'
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Success Stat',
|
label: 'Success Stat',
|
||||||
value: '89%',
|
value: '89%',
|
||||||
desc: 'Success rate',
|
desc: 'Success rate',
|
||||||
icon: Span({ class: 'text-2xl' }, '✅'),
|
icon: Span({ class: 'text-4xl' }, '✅'),
|
||||||
class: 'bg-success/10 text-success'
|
class: 'bg-success/10 text-success'
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Warning Stat',
|
label: 'Warning Stat',
|
||||||
value: '23',
|
value: '23',
|
||||||
desc: 'Pending items',
|
desc: 'Pending items',
|
||||||
icon: Span({ class: 'text-2xl' }, '⚠️'),
|
icon: Span({ class: 'text-4xl' }, '⚠️'),
|
||||||
class: 'bg-warning/10 text-warning'
|
class: 'bg-warning/10 text-warning'
|
||||||
}),
|
}),
|
||||||
Stat({
|
Stat({
|
||||||
label: 'Error Stat',
|
label: 'Error Stat',
|
||||||
value: '5',
|
value: '5',
|
||||||
desc: 'Failed attempts',
|
desc: 'Failed attempts',
|
||||||
icon: Span({ class: 'text-2xl' }, '❌'),
|
icon: Span({ class: 'text-4xl' }, '❌'),
|
||||||
class: 'bg-error/10 text-error'
|
class: 'bg-error/10 text-error'
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compact Stats
|
### Compact Stats
|
||||||
@@ -325,5 +325,5 @@ const CompactDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(CompactDemo, '#demo-compact');
|
Mount(CompactDemo, '#demo-compact');
|
||||||
```
|
```
|
||||||
@@ -50,7 +50,7 @@ const BasicDemo = () => {
|
|||||||
onclick: () => isOn(!isOn())
|
onclick: () => isOn(!isOn())
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Icon Swap
|
### Icon Swap
|
||||||
@@ -73,7 +73,7 @@ const IconsDemo = () => {
|
|||||||
onclick: () => isOn(!isOn())
|
onclick: () => isOn(!isOn())
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Emoji Swap
|
### Emoji Swap
|
||||||
@@ -96,7 +96,7 @@ const EmojiDemo = () => {
|
|||||||
onclick: () => isOn(!isOn())
|
onclick: () => isOn(!isOn())
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(EmojiDemo, '#demo-emoji');
|
Mount(EmojiDemo, '#demo-emoji');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Content Swap
|
### Custom Content Swap
|
||||||
@@ -119,7 +119,7 @@ const CustomDemo = () => {
|
|||||||
onclick: () => isOn(!isOn())
|
onclick: () => isOn(!isOn())
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(CustomDemo, '#demo-custom');
|
Mount(CustomDemo, '#demo-custom');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Reactive State
|
### With Reactive State
|
||||||
@@ -149,7 +149,7 @@ const ReactiveDemo = () => {
|
|||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Toggle Mode Swap
|
### Toggle Mode Swap
|
||||||
@@ -200,7 +200,7 @@ const ModeDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ModeDemo, '#demo-mode');
|
Mount(ModeDemo, '#demo-mode');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -249,7 +249,7 @@ const VariantsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Simple Todo Toggle
|
### Simple Todo Toggle
|
||||||
@@ -288,5 +288,5 @@ const TodoDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(TodoDemo, '#demo-todo');
|
Mount(TodoDemo, '#demo-todo');
|
||||||
```
|
```
|
||||||
@@ -70,7 +70,7 @@ const BasicDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Zebra Stripes
|
### With Zebra Stripes
|
||||||
@@ -101,7 +101,7 @@ const ZebraDemo = () => {
|
|||||||
zebra: true
|
zebra: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(ZebraDemo, '#demo-zebra');
|
Mount(ZebraDemo, '#demo-zebra');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Custom Cell Rendering
|
### With Custom Cell Rendering
|
||||||
@@ -147,7 +147,7 @@ const CustomDemo = () => {
|
|||||||
zebra: true
|
zebra: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(CustomDemo, '#demo-custom');
|
Mount(CustomDemo, '#demo-custom');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Footers
|
### With Footers
|
||||||
@@ -195,7 +195,7 @@ const FooterDemo = () => {
|
|||||||
zebra: true
|
zebra: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(FooterDemo, '#demo-footer');
|
Mount(FooterDemo, '#demo-footer');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Empty State
|
### Empty State
|
||||||
@@ -223,7 +223,7 @@ const EmptyDemo = () => {
|
|||||||
])
|
])
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(EmptyDemo, '#demo-empty');
|
Mount(EmptyDemo, '#demo-empty');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Data
|
### Reactive Data
|
||||||
@@ -294,7 +294,7 @@ const ReactiveDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Actions
|
### With Actions
|
||||||
@@ -354,7 +354,7 @@ const ActionsDemo = () => {
|
|||||||
zebra: true
|
zebra: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
Mount(ActionsDemo, '#demo-actions');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -408,5 +408,5 @@ const VariantsDemo = () => {
|
|||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -87,7 +87,7 @@ const BasicDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Icons
|
### With Icons
|
||||||
@@ -126,7 +126,7 @@ const IconsDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Tooltips
|
### With Tooltips
|
||||||
@@ -168,7 +168,7 @@ const TooltipsDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(TooltipsDemo, '#demo-tooltips');
|
Mount(TooltipsDemo, '#demo-tooltips');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Disabled Tab
|
### Disabled Tab
|
||||||
@@ -207,7 +207,7 @@ const DisabledDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(DisabledDemo, '#demo-disabled');
|
Mount(DisabledDemo, '#demo-disabled');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Content
|
### Reactive Content
|
||||||
@@ -253,7 +253,7 @@ const ReactiveDemo = () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Tabs
|
### Form Tabs
|
||||||
@@ -355,7 +355,7 @@ const FormTabs = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FormTabs, '#demo-form');
|
Mount(FormTabs, '#demo-form');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -372,6 +372,7 @@ const VariantsDemo = () => {
|
|||||||
const active1 = $('tab1');
|
const active1 = $('tab1');
|
||||||
const active2 = $('tab1');
|
const active2 = $('tab1');
|
||||||
const active3 = $('tab1');
|
const active3 = $('tab1');
|
||||||
|
const active4 = $('tab4');
|
||||||
|
|
||||||
const createItems = (active) => [
|
const createItems = (active) => [
|
||||||
{
|
{
|
||||||
@@ -391,6 +392,12 @@ const VariantsDemo = () => {
|
|||||||
active: () => active() === 'tab3',
|
active: () => active() === 'tab3',
|
||||||
onclick: () => active('tab3'),
|
onclick: () => active('tab3'),
|
||||||
content: Div({ class: 'p-4' }, 'Content 3')
|
content: Div({ class: 'p-4' }, 'Content 3')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Tab 4',
|
||||||
|
active: () => active() === 'tab4',
|
||||||
|
onclick: () => active('tab4'),
|
||||||
|
content: Div({ class: 'p-4' }, 'Content 4')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -402,8 +409,11 @@ const VariantsDemo = () => {
|
|||||||
Tabs({ items: createItems(active2), class: 'tabs-box' }),
|
Tabs({ items: createItems(active2), class: 'tabs-box' }),
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Lifted Tabs'),
|
Div({ class: 'text-sm font-bold mt-4' }, 'Lifted Tabs'),
|
||||||
Tabs({ items: createItems(active3), class: 'tabs-lifted' })
|
Tabs({ items: createItems(active3), class: 'tabs-lift' }),
|
||||||
|
|
||||||
|
Div({ class: 'text-sm font-bold mt-4' }, 'Bordered Tabs'),
|
||||||
|
Tabs({ items: createItems(active4), class: 'tabs-border' })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -60,7 +60,7 @@ const BasicDemo = () => {
|
|||||||
|
|
||||||
return Timeline({ items: events });
|
return Timeline({ items: events });
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Horizontal Timeline
|
### Horizontal Timeline
|
||||||
@@ -88,7 +88,7 @@ const HorizontalDemo = () => {
|
|||||||
class: 'min-w-[600px]'
|
class: 'min-w-[600px]'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(HorizontalDemo, '#demo-horizontal');
|
Mount(HorizontalDemo, '#demo-horizontal');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compact Timeline
|
### Compact Timeline
|
||||||
@@ -114,7 +114,7 @@ const CompactDemo = () => {
|
|||||||
compact: true
|
compact: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$mount(CompactDemo, '#demo-compact');
|
Mount(CompactDemo, '#demo-compact');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Icons
|
### Custom Icons
|
||||||
@@ -137,7 +137,7 @@ const IconsDemo = () => {
|
|||||||
|
|
||||||
return Timeline({ items: milestones });
|
return Timeline({ items: milestones });
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reactive Timeline
|
### Reactive Timeline
|
||||||
@@ -192,7 +192,7 @@ const ReactiveDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
Mount(ReactiveDemo, '#demo-reactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Order Status Tracker
|
### Order Status Tracker
|
||||||
@@ -234,7 +234,7 @@ const OrderDemo = () => {
|
|||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(OrderDemo, '#demo-order');
|
Mount(OrderDemo, '#demo-order');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Company History
|
### Company History
|
||||||
@@ -259,7 +259,7 @@ const HistoryDemo = () => {
|
|||||||
|
|
||||||
return Timeline({ items: milestones });
|
return Timeline({ items: milestones });
|
||||||
};
|
};
|
||||||
$mount(HistoryDemo, '#demo-history');
|
Mount(HistoryDemo, '#demo-history');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Variants
|
### All Variants
|
||||||
@@ -290,5 +290,5 @@ const VariantsDemo = () => {
|
|||||||
Timeline({ items: sampleItems, compact: true })
|
Timeline({ items: sampleItems, compact: true })
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
Mount(VariantsDemo, '#demo-variants');
|
||||||
```
|
```
|
||||||
@@ -44,7 +44,7 @@ const BasicDemo = () => {
|
|||||||
}, 'Error Toast')
|
}, 'Error Toast')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Different Durations
|
### Different Durations
|
||||||
@@ -77,7 +77,7 @@ const DurationDemo = () => {
|
|||||||
}, '8 Seconds')
|
}, '8 Seconds')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(DurationDemo, '#demo-duration');
|
Mount(DurationDemo, '#demo-duration');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interactive Toast
|
### Interactive Toast
|
||||||
@@ -115,7 +115,7 @@ const InteractiveDemo = () => {
|
|||||||
Div({ class: 'text-sm opacity-70' }, () => `Toasts shown: ${count()}`)
|
Div({ class: 'text-sm opacity-70' }, () => `Toasts shown: ${count()}`)
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
Mount(InteractiveDemo, '#demo-interactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Validation Toast
|
### Form Validation Toast
|
||||||
@@ -174,7 +174,7 @@ const FormToastDemo = () => {
|
|||||||
}, 'Login')
|
}, 'Login')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FormToastDemo, '#demo-form');
|
Mount(FormToastDemo, '#demo-form');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Success Feedback
|
### Success Feedback
|
||||||
@@ -227,7 +227,7 @@ const FeedbackDemo = () => {
|
|||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FeedbackDemo, '#demo-feedback');
|
Mount(FeedbackDemo, '#demo-feedback');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Error Handling
|
### Error Handling
|
||||||
@@ -274,7 +274,7 @@ const ErrorDemo = () => {
|
|||||||
}, 'Timeout')
|
}, 'Timeout')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ErrorDemo, '#demo-error');
|
Mount(ErrorDemo, '#demo-error');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Messages
|
### Custom Messages
|
||||||
@@ -311,7 +311,7 @@ const CustomDemo = () => {
|
|||||||
}, 'Security Alert')
|
}, 'Security Alert')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(CustomDemo, '#demo-custom');
|
Mount(CustomDemo, '#demo-custom');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multiple Toasts
|
### Multiple Toasts
|
||||||
@@ -339,5 +339,5 @@ const MultipleDemo = () => {
|
|||||||
}, 'Show Multiple Toasts')
|
}, 'Show Multiple Toasts')
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(MultipleDemo, '#demo-multiple');
|
Mount(MultipleDemo, '#demo-multiple');
|
||||||
```
|
```
|
||||||
@@ -58,7 +58,7 @@ const BasicDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(BasicDemo, '#demo-basic');
|
Mount(BasicDemo, '#demo-basic');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tooltip Positions
|
### Tooltip Positions
|
||||||
@@ -87,7 +87,7 @@ const PositionsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(PositionsDemo, '#demo-positions');
|
Mount(PositionsDemo, '#demo-positions');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tooltip with Icons
|
### Tooltip with Icons
|
||||||
@@ -116,7 +116,7 @@ const IconsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(IconsDemo, '#demo-icons');
|
Mount(IconsDemo, '#demo-icons');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Field Tooltips
|
### Form Field Tooltips
|
||||||
@@ -159,7 +159,7 @@ const FormDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(FormDemo, '#demo-form');
|
Mount(FormDemo, '#demo-form');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Interactive Tooltip
|
### Interactive Tooltip
|
||||||
@@ -201,7 +201,7 @@ const InteractiveDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
Mount(InteractiveDemo, '#demo-interactive');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rich Tooltip Content
|
### Rich Tooltip Content
|
||||||
@@ -240,7 +240,7 @@ const RichDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(RichDemo, '#demo-rich');
|
Mount(RichDemo, '#demo-rich');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Color Variants
|
### Color Variants
|
||||||
@@ -278,7 +278,7 @@ const ColorsDemo = () => {
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(ColorsDemo, '#demo-colors');
|
Mount(ColorsDemo, '#demo-colors');
|
||||||
```
|
```
|
||||||
|
|
||||||
### All Tooltip Positions
|
### All Tooltip Positions
|
||||||
@@ -294,26 +294,26 @@ $mount(ColorsDemo, '#demo-colors');
|
|||||||
const AllPositionsDemo = () => {
|
const AllPositionsDemo = () => {
|
||||||
return Div({ class: 'grid grid-cols-3 gap-4 justify-items-center' }, [
|
return Div({ class: 'grid grid-cols-3 gap-4 justify-items-center' }, [
|
||||||
Div({ class: 'col-start-2' }, [
|
Div({ class: 'col-start-2' }, [
|
||||||
Tooltip({ tip: 'Top tooltip', ui: 'tooltip-top' }, [
|
Tooltip({ tip: 'Top tooltip', class: 'tooltip-top' }, [
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Top')
|
Button({ class: 'btn btn-sm w-24' }, 'Top')
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
Div({ class: 'col-start-1 row-start-2' }, [
|
Div({ class: 'col-start-1 row-start-2' }, [
|
||||||
Tooltip({ tip: 'Left tooltip', ui: 'tooltip-left' }, [
|
Tooltip({ tip: 'Left tooltip', class: 'tooltip-left' }, [
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Left')
|
Button({ class: 'btn btn-sm w-24' }, 'Left')
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
Div({ class: 'col-start-3 row-start-2' }, [
|
Div({ class: 'col-start-3 row-start-2' }, [
|
||||||
Tooltip({ tip: 'Right tooltip', ui: 'tooltip-right' }, [
|
Tooltip({ tip: 'Right tooltip', class: 'tooltip-right' }, [
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Right')
|
Button({ class: 'btn btn-sm w-24' }, 'Right')
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
Div({ class: 'col-start-2 row-start-3' }, [
|
Div({ class: 'col-start-2 row-start-3' }, [
|
||||||
Tooltip({ tip: 'Bottom tooltip', ui: 'tooltip-bottom' }, [
|
Tooltip({ tip: 'Bottom tooltip', class: 'tooltip-bottom' }, [
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Bottom')
|
Button({ class: 'btn btn-sm w-24' }, 'Bottom')
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$mount(AllPositionsDemo, '#demo-all-positions');
|
Mount(AllPositionsDemo, '#demo-all-positions');
|
||||||
```
|
```
|
||||||
@@ -1,730 +0,0 @@
|
|||||||
# Accordion
|
|
||||||
|
|
||||||
Collapsible accordion component for organizing content into expandable sections with DaisyUI styling.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Accordion`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `title` | `string \| VNode \| Signal` | Required | Accordion section title |
|
|
||||||
| `open` | `boolean \| Signal<boolean>` | `false` | Whether the accordion is expanded |
|
|
||||||
| `name` | `string` | `-` | Group name for radio-style accordions (only one open at a time) |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `VNode \| Array<VNode>` | Required | Content to display when expanded |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Accordion
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const open1 = $(false);
|
|
||||||
const open2 = $(false);
|
|
||||||
const open3 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 1',
|
|
||||||
open: open1,
|
|
||||||
onclick: () => open1(!open1())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 1. This is a basic accordion section.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 2',
|
|
||||||
open: open2,
|
|
||||||
onclick: () => open2(!open2())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 2. You can put any content here.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 3',
|
|
||||||
open: open3,
|
|
||||||
onclick: () => open3(!open3())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 3. Accordions are great for FAQs.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Group Accordion (Radio Style)
|
|
||||||
|
|
||||||
<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-group" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const GroupDemo = () => {
|
|
||||||
const openSection = $('section1');
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 1',
|
|
||||||
name: 'group',
|
|
||||||
open: () => openSection() === 'section1',
|
|
||||||
onclick: () => openSection('section1')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 1. Only one section can be open at a time.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 2',
|
|
||||||
name: 'group',
|
|
||||||
open: () => openSection() === 'section2',
|
|
||||||
onclick: () => openSection('section2')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 2. Opening this will close section 1.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 3',
|
|
||||||
name: 'group',
|
|
||||||
open: () => openSection() === 'section3',
|
|
||||||
onclick: () => openSection('section3')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 3. This is useful for FAQ sections.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(GroupDemo, '#demo-group');
|
|
||||||
```
|
|
||||||
|
|
||||||
### FAQ Accordion
|
|
||||||
|
|
||||||
<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-faq" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const FaqDemo = () => {
|
|
||||||
const openFaq = $('faq1');
|
|
||||||
|
|
||||||
const faqs = [
|
|
||||||
{ id: 'faq1', question: 'What is this component?', answer: 'This is an accordion component built with DaisyUI and Tailwind CSS for creating collapsible content sections.' },
|
|
||||||
{ id: 'faq2', question: 'How do I use it?', answer: 'Simply import the Accordion component and pass title and children props. Use the open prop to control expansion.' },
|
|
||||||
{ id: 'faq3', question: 'Can I have multiple open?', answer: 'Yes! By default, accordions can be opened independently. Use the name prop to create groups where only one can be open.' },
|
|
||||||
{ id: 'faq4', question: 'Is it accessible?', answer: 'Yes, the accordion uses proper ARIA attributes and keyboard navigation support.' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, faqs.map(faq =>
|
|
||||||
Accordion({
|
|
||||||
title: faq.question,
|
|
||||||
name: 'faq-group',
|
|
||||||
open: () => openFaq() === faq.id,
|
|
||||||
onclick: () => openFaq(openFaq() === faq.id ? '' : faq.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2 text-sm' }, faq.answer)
|
|
||||||
])
|
|
||||||
));
|
|
||||||
};
|
|
||||||
$mount(FaqDemo, '#demo-faq');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Rich Content
|
|
||||||
|
|
||||||
<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-rich" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const RichDemo = () => {
|
|
||||||
const open1 = $(true);
|
|
||||||
const open2 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['📊', 'Statistics']),
|
|
||||||
open: open1,
|
|
||||||
onclick: () => open1(!open1())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, [
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4' }, [
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Users'),
|
|
||||||
Div({ class: 'stat-value text-lg' }, '1,234')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Revenue'),
|
|
||||||
Div({ class: 'stat-value text-lg' }, '$45,678')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Growth'),
|
|
||||||
Div({ class: 'stat-value text-lg text-success' }, '+23%')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Active'),
|
|
||||||
Div({ class: 'stat-value text-lg' }, '89%')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['👥', 'Team Members']),
|
|
||||||
open: open2,
|
|
||||||
onclick: () => open2(!open2())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2 space-y-2' }, [
|
|
||||||
Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JD')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, 'John Doe'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Developer')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-secondary text-secondary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JS')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, 'Jane Smith'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Designer')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(RichDemo, '#demo-rich');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Form Accordion
|
|
||||||
|
|
||||||
<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 FormAccordion = () => {
|
|
||||||
const openStep = $('step1');
|
|
||||||
const formData = $({
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
address: '',
|
|
||||||
payment: 'credit'
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateField = (field, value) => {
|
|
||||||
formData({ ...formData(), [field]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const nextStep = () => {
|
|
||||||
if (openStep() === 'step1') openStep('step2');
|
|
||||||
else if (openStep() === 'step2') openStep('step3');
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevStep = () => {
|
|
||||||
if (openStep() === 'step2') openStep('step1');
|
|
||||||
else if (openStep() === 'step3') openStep('step2');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
Toast('Form submitted!', 'alert-success', 2000);
|
|
||||||
console.log(formData());
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['1️⃣', 'Personal Information']),
|
|
||||||
name: 'form-steps',
|
|
||||||
open: () => openStep() === 'step1',
|
|
||||||
onclick: () => openStep('step1')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Full Name',
|
|
||||||
value: () => formData().name,
|
|
||||||
placeholder: 'Enter your name',
|
|
||||||
oninput: (e) => updateField('name', e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: () => formData().email,
|
|
||||||
placeholder: 'email@example.com',
|
|
||||||
oninput: (e) => updateField('email', e.target.value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'flex justify-end mt-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-sm',
|
|
||||||
onclick: nextStep,
|
|
||||||
disabled: () => !formData().name || !formData().email
|
|
||||||
}, 'Next →')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['2️⃣', 'Address']),
|
|
||||||
name: 'form-steps',
|
|
||||||
open: () => openStep() === 'step2',
|
|
||||||
onclick: () => openStep('step2')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Address',
|
|
||||||
value: () => formData().address,
|
|
||||||
placeholder: 'Street address',
|
|
||||||
oninput: (e) => updateField('address', e.target.value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'flex justify-between mt-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-sm',
|
|
||||||
onclick: nextStep
|
|
||||||
}, 'Next →')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['3️⃣', 'Payment']),
|
|
||||||
name: 'form-steps',
|
|
||||||
open: () => openStep() === 'step3',
|
|
||||||
onclick: () => openStep('step3')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Radio({
|
|
||||||
label: 'Credit Card',
|
|
||||||
value: () => formData().payment,
|
|
||||||
radioValue: 'credit',
|
|
||||||
onclick: () => updateField('payment', 'credit')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'PayPal',
|
|
||||||
value: () => formData().payment,
|
|
||||||
radioValue: 'paypal',
|
|
||||||
onclick: () => updateField('payment', 'paypal')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Bank Transfer',
|
|
||||||
value: () => formData().payment,
|
|
||||||
radioValue: 'bank',
|
|
||||||
onclick: () => updateField('payment', 'bank')
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between mt-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'),
|
|
||||||
Button({ class: 'btn btn-success btn-sm', onclick: handleSubmit }, 'Submit')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormAccordion, '#demo-form');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-6"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const open1 = $(true);
|
|
||||||
const open2 = $(false);
|
|
||||||
const open3 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Accordion'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({ title: 'Default style', open: open1, onclick: () => open1(!open1()) }, [
|
|
||||||
Div({ class: 'p-2' }, 'Default accordion with standard styling.')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'Custom Styling'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'text-primary font-bold' }, 'Primary Title'),
|
|
||||||
open: open2,
|
|
||||||
onclick: () => open2(!open2()),
|
|
||||||
class: 'bg-primary/5 border-primary/20'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Accordion with custom styling and primary color.')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Icons'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['✨', 'Featured Content']),
|
|
||||||
open: open3,
|
|
||||||
onclick: () => open3(!open3())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Accordion with emoji icons in the title.')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initAccordionExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Accordion
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const open1 = $(false);
|
|
||||||
const open2 = $(false);
|
|
||||||
const open3 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 1',
|
|
||||||
open: open1,
|
|
||||||
onclick: () => open1(!open1())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 1. This is a basic accordion section.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 2',
|
|
||||||
open: open2,
|
|
||||||
onclick: () => open2(!open2())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 2. You can put any content here.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 3',
|
|
||||||
open: open3,
|
|
||||||
onclick: () => open3(!open3())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 3. Accordions are great for FAQs.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Group Accordion (Radio Style)
|
|
||||||
const groupTarget = document.querySelector('#demo-group');
|
|
||||||
if (groupTarget && !groupTarget.hasChildNodes()) {
|
|
||||||
const GroupDemo = () => {
|
|
||||||
const openSection = $('section1');
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 1',
|
|
||||||
name: 'group',
|
|
||||||
open: () => openSection() === 'section1',
|
|
||||||
onclick: () => openSection('section1')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 1. Only one section can be open at a time.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 2',
|
|
||||||
name: 'group',
|
|
||||||
open: () => openSection() === 'section2',
|
|
||||||
onclick: () => openSection('section2')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 2. Opening this will close section 1.')
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: 'Section 3',
|
|
||||||
name: 'group',
|
|
||||||
open: () => openSection() === 'section3',
|
|
||||||
onclick: () => openSection('section3')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Content for section 3. This is useful for FAQ sections.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(GroupDemo, groupTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. FAQ Accordion
|
|
||||||
const faqTarget = document.querySelector('#demo-faq');
|
|
||||||
if (faqTarget && !faqTarget.hasChildNodes()) {
|
|
||||||
const FaqDemo = () => {
|
|
||||||
const openFaq = $('faq1');
|
|
||||||
|
|
||||||
const faqs = [
|
|
||||||
{ id: 'faq1', question: 'What is this component?', answer: 'This is an accordion component built with DaisyUI and Tailwind CSS for creating collapsible content sections.' },
|
|
||||||
{ id: 'faq2', question: 'How do I use it?', answer: 'Simply import the Accordion component and pass title and children props. Use the open prop to control expansion.' },
|
|
||||||
{ id: 'faq3', question: 'Can I have multiple open?', answer: 'Yes! By default, accordions can be opened independently. Use the name prop to create groups where only one can be open.' },
|
|
||||||
{ id: 'faq4', question: 'Is it accessible?', answer: 'Yes, the accordion uses proper ARIA attributes and keyboard navigation support.' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, faqs.map(faq =>
|
|
||||||
Accordion({
|
|
||||||
title: faq.question,
|
|
||||||
name: 'faq-group',
|
|
||||||
open: () => openFaq() === faq.id,
|
|
||||||
onclick: () => openFaq(openFaq() === faq.id ? '' : faq.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2 text-sm' }, faq.answer)
|
|
||||||
])
|
|
||||||
));
|
|
||||||
};
|
|
||||||
$mount(FaqDemo, faqTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. With Rich Content
|
|
||||||
const richTarget = document.querySelector('#demo-rich');
|
|
||||||
if (richTarget && !richTarget.hasChildNodes()) {
|
|
||||||
const RichDemo = () => {
|
|
||||||
const open1 = $(true);
|
|
||||||
const open2 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['📊', 'Statistics']),
|
|
||||||
open: open1,
|
|
||||||
onclick: () => open1(!open1())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, [
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4' }, [
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Users'),
|
|
||||||
Div({ class: 'stat-value text-lg' }, '1,234')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Revenue'),
|
|
||||||
Div({ class: 'stat-value text-lg' }, '$45,678')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Growth'),
|
|
||||||
Div({ class: 'stat-value text-lg text-success' }, '+23%')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-100 rounded-lg p-3' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Active'),
|
|
||||||
Div({ class: 'stat-value text-lg' }, '89%')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['👥', 'Team Members']),
|
|
||||||
open: open2,
|
|
||||||
onclick: () => open2(!open2())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2 space-y-2' }, [
|
|
||||||
Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JD')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, 'John Doe'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Developer')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-3 p-2 hover:bg-base-100 rounded-lg' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-secondary text-secondary-content rounded-full w-10 h-10 flex items-center justify-center' }, 'JS')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, 'Jane Smith'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Designer')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(RichDemo, richTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Form Accordion
|
|
||||||
const formTarget = document.querySelector('#demo-form');
|
|
||||||
if (formTarget && !formTarget.hasChildNodes()) {
|
|
||||||
const FormAccordion = () => {
|
|
||||||
const openStep = $('step1');
|
|
||||||
const formData = $({
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
address: '',
|
|
||||||
payment: 'credit'
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateField = (field, value) => {
|
|
||||||
formData({ ...formData(), [field]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const nextStep = () => {
|
|
||||||
if (openStep() === 'step1') openStep('step2');
|
|
||||||
else if (openStep() === 'step2') openStep('step3');
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevStep = () => {
|
|
||||||
if (openStep() === 'step2') openStep('step1');
|
|
||||||
else if (openStep() === 'step3') openStep('step2');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
Toast('Form submitted!', 'alert-success', 2000);
|
|
||||||
console.log(formData());
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['1️⃣', 'Personal Information']),
|
|
||||||
name: 'form-steps',
|
|
||||||
open: () => openStep() === 'step1',
|
|
||||||
onclick: () => openStep('step1')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Full Name',
|
|
||||||
value: () => formData().name,
|
|
||||||
placeholder: 'Enter your name',
|
|
||||||
oninput: (e) => updateField('name', e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: () => formData().email,
|
|
||||||
placeholder: 'email@example.com',
|
|
||||||
oninput: (e) => updateField('email', e.target.value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'flex justify-end mt-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-sm',
|
|
||||||
onclick: nextStep,
|
|
||||||
disabled: () => !formData().name || !formData().email
|
|
||||||
}, 'Next →')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['2️⃣', 'Address']),
|
|
||||||
name: 'form-steps',
|
|
||||||
open: () => openStep() === 'step2',
|
|
||||||
onclick: () => openStep('step2')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Address',
|
|
||||||
value: () => formData().address,
|
|
||||||
placeholder: 'Street address',
|
|
||||||
oninput: (e) => updateField('address', e.target.value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'flex justify-between mt-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-sm',
|
|
||||||
onclick: nextStep
|
|
||||||
}, 'Next →')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['3️⃣', 'Payment']),
|
|
||||||
name: 'form-steps',
|
|
||||||
open: () => openStep() === 'step3',
|
|
||||||
onclick: () => openStep('step3')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Radio({
|
|
||||||
label: 'Credit Card',
|
|
||||||
value: () => formData().payment,
|
|
||||||
radioValue: 'credit',
|
|
||||||
onclick: () => updateField('payment', 'credit')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'PayPal',
|
|
||||||
value: () => formData().payment,
|
|
||||||
radioValue: 'paypal',
|
|
||||||
onclick: () => updateField('payment', 'paypal')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Bank Transfer',
|
|
||||||
value: () => formData().payment,
|
|
||||||
radioValue: 'bank',
|
|
||||||
onclick: () => updateField('payment', 'bank')
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between mt-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm', onclick: prevStep }, '← Back'),
|
|
||||||
Button({ class: 'btn btn-success btn-sm', onclick: handleSubmit }, 'Submit')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormAccordion, formTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const open1 = $(true);
|
|
||||||
const open2 = $(false);
|
|
||||||
const open3 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Accordion'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({ title: 'Default style', open: open1, onclick: () => open1(!open1()) }, [
|
|
||||||
Div({ class: 'p-2' }, 'Default accordion with standard styling.')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'Custom Styling'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'text-primary font-bold' }, 'Primary Title'),
|
|
||||||
open: open2,
|
|
||||||
onclick: () => open2(!open2()),
|
|
||||||
class: 'bg-primary/5 border-primary/20'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Accordion with custom styling and primary color.')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Icons'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Accordion({
|
|
||||||
title: Span({ class: 'flex items-center gap-2' }, ['✨', 'Featured Content']),
|
|
||||||
open: open3,
|
|
||||||
onclick: () => open3(!open3())
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-2' }, 'Accordion with emoji icons in the title.')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initAccordionExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initAccordionExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,521 +0,0 @@
|
|||||||
# Alert
|
|
||||||
|
|
||||||
Alert component for displaying contextual messages, notifications, and feedback with different severity levels. Supports soft and solid variants.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Alert`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `type` | `string` | `'info'` | Alert type: 'info', 'success', 'warning', 'error' |
|
|
||||||
| `soft` | `boolean \| Signal<boolean>` | `true` | Use soft variant (subtle background) |
|
|
||||||
| `actions` | `VNode \| function` | `-` | Optional action buttons or content |
|
|
||||||
| `message` | `string \| VNode \| Signal` | `-` | Alert message content |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `string \| VNode` | `-` | Alert content (alternative to `message`) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Alerts
|
|
||||||
|
|
||||||
<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 = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', message: 'This is an informational message.' }),
|
|
||||||
Alert({ type: 'success', message: 'Operation completed successfully!' }),
|
|
||||||
Alert({ type: 'warning', message: 'Please review your input before proceeding.' }),
|
|
||||||
Alert({ type: 'error', message: 'An error occurred while processing your request.' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Soft vs Solid 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-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', soft: true, message: 'Soft info alert (default)' }),
|
|
||||||
Alert({ type: 'info', soft: false, message: 'Solid info alert' }),
|
|
||||||
Alert({ type: 'success', soft: true, message: 'Soft success alert' }),
|
|
||||||
Alert({ type: 'success', soft: false, message: 'Solid success alert' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Actions
|
|
||||||
|
|
||||||
<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-actions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const showUndo = $(false);
|
|
||||||
const deletedItem = $(null);
|
|
||||||
|
|
||||||
const deleteItem = (item) => {
|
|
||||||
deletedItem(item);
|
|
||||||
showUndo(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
if (showUndo()) {
|
|
||||||
showUndo(false);
|
|
||||||
Toast('Item permanently deleted', 'alert-info', 2000);
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const undoDelete = () => {
|
|
||||||
showUndo(false);
|
|
||||||
Toast(`Restored: ${deletedItem()}`, 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document A') }, 'Delete Document A'),
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document B') }, 'Delete Document B')
|
|
||||||
]),
|
|
||||||
() => showUndo() ? Alert({
|
|
||||||
type: 'warning',
|
|
||||||
soft: true,
|
|
||||||
message: `Deleted: ${deletedItem()}`,
|
|
||||||
actions: Button({
|
|
||||||
class: 'btn btn-sm btn-primary',
|
|
||||||
onclick: undoDelete
|
|
||||||
}, 'Undo')
|
|
||||||
}) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dismissible Alert
|
|
||||||
|
|
||||||
<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-dismissible" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DismissibleDemo = () => {
|
|
||||||
const visible = $(true);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
() => visible() ? Alert({
|
|
||||||
type: 'info',
|
|
||||||
message: 'This alert can be dismissed. Click the X button to close.',
|
|
||||||
actions: Button({
|
|
||||||
class: 'btn btn-xs btn-circle btn-ghost',
|
|
||||||
onclick: () => visible(false)
|
|
||||||
}, '✕')
|
|
||||||
}) : null,
|
|
||||||
() => !visible() ? Button({
|
|
||||||
class: 'btn btn-sm btn-ghost',
|
|
||||||
onclick: () => visible(true)
|
|
||||||
}, 'Show Alert') : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DismissibleDemo, '#demo-dismissible');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Alert
|
|
||||||
|
|
||||||
<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 email = $('');
|
|
||||||
const error = $('');
|
|
||||||
|
|
||||||
const validateEmail = (value) => {
|
|
||||||
email(value);
|
|
||||||
if (value && !value.includes('@')) {
|
|
||||||
error('Please enter a valid email address');
|
|
||||||
} else {
|
|
||||||
error('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Email Address',
|
|
||||||
placeholder: 'Enter your email',
|
|
||||||
value: email,
|
|
||||||
oninput: (e) => validateEmail(e.target.value)
|
|
||||||
}),
|
|
||||||
() => error() ? Alert({ type: 'error', message: error() }) : null,
|
|
||||||
() => email() && !error() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: `Valid email: ${email()}`
|
|
||||||
}) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Form Validation
|
|
||||||
|
|
||||||
<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 name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const submitted = $(false);
|
|
||||||
const errors = $({ name: '', email: '' });
|
|
||||||
|
|
||||||
const validate = () => {
|
|
||||||
const newErrors = {
|
|
||||||
name: name().trim() ? '' : 'Name is required',
|
|
||||||
email: email().trim() ? (email().includes('@') ? '' : 'Invalid email') : 'Email is required'
|
|
||||||
};
|
|
||||||
errors(newErrors);
|
|
||||||
return !newErrors.name && !newErrors.email;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (validate()) {
|
|
||||||
submitted(true);
|
|
||||||
setTimeout(() => submitted(false), 3000);
|
|
||||||
Toast('Form submitted successfully!', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'text-lg font-bold' }, 'Contact Form'),
|
|
||||||
Input({
|
|
||||||
label: 'Name',
|
|
||||||
value: name,
|
|
||||||
error: () => errors().name,
|
|
||||||
oninput: (e) => {
|
|
||||||
name(e.target.value);
|
|
||||||
validate();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
value: email,
|
|
||||||
error: () => errors().email,
|
|
||||||
oninput: (e) => {
|
|
||||||
email(e.target.value);
|
|
||||||
validate();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Button({ class: 'btn btn-primary', onclick: handleSubmit }, 'Submit'),
|
|
||||||
() => submitted() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: 'Thank you! We will contact you soon.'
|
|
||||||
}) : null,
|
|
||||||
() => (errors().name || errors().email) ? Alert({
|
|
||||||
type: 'error',
|
|
||||||
message: 'Please fix the errors above before submitting.'
|
|
||||||
}) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormDemo, '#demo-form');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Icon Alerts
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', message: 'Information alert with custom icon' }),
|
|
||||||
Alert({ type: 'success', message: 'Success alert with custom icon' }),
|
|
||||||
Alert({ type: 'warning', message: 'Warning alert with custom icon' }),
|
|
||||||
Alert({ type: 'error', message: 'Error alert with custom icon' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### All Types
|
|
||||||
|
|
||||||
<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-all" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AllTypesDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', message: 'ℹ️ This is an info alert' }),
|
|
||||||
Alert({ type: 'success', message: '✅ This is a success alert' }),
|
|
||||||
Alert({ type: 'warning', message: '⚠️ This is a warning alert' }),
|
|
||||||
Alert({ type: 'error', message: '❌ This is an error alert' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AllTypesDemo, '#demo-all');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initAlertExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Alerts
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', message: 'This is an informational message.' }),
|
|
||||||
Alert({ type: 'success', message: 'Operation completed successfully!' }),
|
|
||||||
Alert({ type: 'warning', message: 'Please review your input before proceeding.' }),
|
|
||||||
Alert({ type: 'error', message: 'An error occurred while processing your request.' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Soft vs Solid Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', soft: true, message: 'Soft info alert (default)' }),
|
|
||||||
Alert({ type: 'info', soft: false, message: 'Solid info alert' }),
|
|
||||||
Alert({ type: 'success', soft: true, message: 'Soft success alert' }),
|
|
||||||
Alert({ type: 'success', soft: false, message: 'Solid success alert' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. With Actions
|
|
||||||
const actionsTarget = document.querySelector('#demo-actions');
|
|
||||||
if (actionsTarget && !actionsTarget.hasChildNodes()) {
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const showUndo = $(false);
|
|
||||||
const deletedItem = $(null);
|
|
||||||
|
|
||||||
const deleteItem = (item) => {
|
|
||||||
deletedItem(item);
|
|
||||||
showUndo(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
if (showUndo()) {
|
|
||||||
showUndo(false);
|
|
||||||
Toast('Item permanently deleted', 'alert-info', 2000);
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const undoDelete = () => {
|
|
||||||
showUndo(false);
|
|
||||||
Toast(`Restored: ${deletedItem()}`, 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document A') }, 'Delete Document A'),
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document B') }, 'Delete Document B')
|
|
||||||
]),
|
|
||||||
() => showUndo() ? Alert({
|
|
||||||
type: 'warning',
|
|
||||||
soft: true,
|
|
||||||
message: `Deleted: ${deletedItem()}`,
|
|
||||||
actions: Button({
|
|
||||||
class: 'btn btn-sm btn-primary',
|
|
||||||
onclick: undoDelete
|
|
||||||
}, 'Undo')
|
|
||||||
}) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, actionsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Dismissible Alert
|
|
||||||
const dismissibleTarget = document.querySelector('#demo-dismissible');
|
|
||||||
if (dismissibleTarget && !dismissibleTarget.hasChildNodes()) {
|
|
||||||
const DismissibleDemo = () => {
|
|
||||||
const visible = $(true);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
() => visible() ? Alert({
|
|
||||||
type: 'info',
|
|
||||||
message: 'This alert can be dismissed. Click the X button to close.',
|
|
||||||
actions: Button({
|
|
||||||
class: 'btn btn-xs btn-circle btn-ghost',
|
|
||||||
onclick: () => visible(false)
|
|
||||||
}, '✕')
|
|
||||||
}) : null,
|
|
||||||
() => !visible() ? Button({
|
|
||||||
class: 'btn btn-sm btn-ghost',
|
|
||||||
onclick: () => visible(true)
|
|
||||||
}, 'Show Alert') : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DismissibleDemo, dismissibleTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Reactive Alert
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const email = $('');
|
|
||||||
const error = $('');
|
|
||||||
|
|
||||||
const validateEmail = (value) => {
|
|
||||||
email(value);
|
|
||||||
if (value && !value.includes('@')) {
|
|
||||||
error('Please enter a valid email address');
|
|
||||||
} else {
|
|
||||||
error('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Email Address',
|
|
||||||
placeholder: 'Enter your email',
|
|
||||||
value: email,
|
|
||||||
oninput: (e) => validateEmail(e.target.value)
|
|
||||||
}),
|
|
||||||
() => error() ? Alert({ type: 'error', message: error() }) : null,
|
|
||||||
() => email() && !error() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: `Valid email: ${email()}`
|
|
||||||
}) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Form Validation
|
|
||||||
const formTarget = document.querySelector('#demo-form');
|
|
||||||
if (formTarget && !formTarget.hasChildNodes()) {
|
|
||||||
const FormDemo = () => {
|
|
||||||
const name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const submitted = $(false);
|
|
||||||
const errors = $({ name: '', email: '' });
|
|
||||||
|
|
||||||
const validate = () => {
|
|
||||||
const newErrors = {
|
|
||||||
name: name().trim() ? '' : 'Name is required',
|
|
||||||
email: email().trim() ? (email().includes('@') ? '' : 'Invalid email') : 'Email is required'
|
|
||||||
};
|
|
||||||
errors(newErrors);
|
|
||||||
return !newErrors.name && !newErrors.email;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (validate()) {
|
|
||||||
submitted(true);
|
|
||||||
setTimeout(() => submitted(false), 3000);
|
|
||||||
Toast('Form submitted successfully!', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'text-lg font-bold' }, 'Contact Form'),
|
|
||||||
Input({
|
|
||||||
label: 'Name',
|
|
||||||
value: name,
|
|
||||||
error: () => errors().name,
|
|
||||||
oninput: (e) => {
|
|
||||||
name(e.target.value);
|
|
||||||
validate();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
value: email,
|
|
||||||
error: () => errors().email,
|
|
||||||
oninput: (e) => {
|
|
||||||
email(e.target.value);
|
|
||||||
validate();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Button({ class: 'btn btn-primary', onclick: handleSubmit }, 'Submit'),
|
|
||||||
() => submitted() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: 'Thank you! We will contact you soon.'
|
|
||||||
}) : null,
|
|
||||||
() => (errors().name || errors().email) ? Alert({
|
|
||||||
type: 'error',
|
|
||||||
message: 'Please fix the errors above before submitting.'
|
|
||||||
}) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormDemo, formTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Icon Alerts
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', message: 'Information alert with custom icon' }),
|
|
||||||
Alert({ type: 'success', message: 'Success alert with custom icon' }),
|
|
||||||
Alert({ type: 'warning', message: 'Warning alert with custom icon' }),
|
|
||||||
Alert({ type: 'error', message: 'Error alert with custom icon' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Types
|
|
||||||
const allTarget = document.querySelector('#demo-all');
|
|
||||||
if (allTarget && !allTarget.hasChildNodes()) {
|
|
||||||
const AllTypesDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Alert({ type: 'info', message: 'ℹ️ This is an info alert' }),
|
|
||||||
Alert({ type: 'success', message: '✅ This is a success alert' }),
|
|
||||||
Alert({ type: 'warning', message: '⚠️ This is a warning alert' }),
|
|
||||||
Alert({ type: 'error', message: '❌ This is an error alert' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AllTypesDemo, allTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initAlertExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initAlertExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,376 +0,0 @@
|
|||||||
# 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>
|
|
||||||
@@ -1,537 +0,0 @@
|
|||||||
# Badge
|
|
||||||
|
|
||||||
Badge component for displaying counts, labels, and status indicators with DaisyUI styling.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Badge`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI badge variants) |
|
|
||||||
| `children` | `string \| VNode` | `-` | Badge content |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Badge
|
|
||||||
|
|
||||||
<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-wrap gap-2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({}, 'Default'),
|
|
||||||
Badge({ class: 'badge-primary' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-secondary' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-accent' }, 'Accent'),
|
|
||||||
Badge({ class: 'badge-info' }, 'Info'),
|
|
||||||
Badge({ class: 'badge-success' }, 'Success'),
|
|
||||||
Badge({ class: 'badge-warning' }, 'Warning'),
|
|
||||||
Badge({ class: 'badge-error' }, 'Error')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Badge Sizes
|
|
||||||
|
|
||||||
<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-sizes" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-2 items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const SizesDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 items-center' }, [
|
|
||||||
Badge({ class: 'badge-xs' }, 'Extra Small'),
|
|
||||||
Badge({ class: 'badge-sm' }, 'Small'),
|
|
||||||
Badge({}, 'Default'),
|
|
||||||
Badge({ class: 'badge-md' }, 'Medium'),
|
|
||||||
Badge({ class: 'badge-lg' }, 'Large')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SizesDemo, '#demo-sizes');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Outline Badges
|
|
||||||
|
|
||||||
<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-outline" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const OutlineDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-outline' }, 'Default'),
|
|
||||||
Badge({ class: 'badge-outline badge-primary' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-outline badge-secondary' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-outline badge-accent' }, 'Accent'),
|
|
||||||
Badge({ class: 'badge-outline badge-info' }, 'Info'),
|
|
||||||
Badge({ class: 'badge-outline badge-success' }, 'Success'),
|
|
||||||
Badge({ class: 'badge-outline badge-warning' }, 'Warning'),
|
|
||||||
Badge({ class: 'badge-outline badge-error' }, 'Error')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(OutlineDemo, '#demo-outline');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ghost Badges
|
|
||||||
|
|
||||||
<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-ghost" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const GhostDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-ghost' }, 'Default'),
|
|
||||||
Badge({ class: 'badge-ghost badge-primary' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-ghost badge-secondary' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-ghost badge-accent' }, 'Accent'),
|
|
||||||
Badge({ class: 'badge-ghost badge-info' }, 'Info'),
|
|
||||||
Badge({ class: 'badge-ghost badge-success' }, 'Success'),
|
|
||||||
Badge({ class: 'badge-ghost badge-warning' }, 'Warning'),
|
|
||||||
Badge({ class: 'badge-ghost badge-error' }, 'Error')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(GhostDemo, '#demo-ghost');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Icons
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-2"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'gap-1' }, [
|
|
||||||
Icons.iconSuccess,
|
|
||||||
Span({}, 'Success')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1 badge-warning' }, [
|
|
||||||
Icons.iconWarning,
|
|
||||||
Span({}, 'Warning')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1 badge-error' }, [
|
|
||||||
Icons.iconError,
|
|
||||||
Span({}, 'Error')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1 badge-info' }, [
|
|
||||||
Icons.iconInfo,
|
|
||||||
Span({}, 'Info')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1' }, [
|
|
||||||
Span({}, '★'),
|
|
||||||
Span({}, '4.5')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Status Badges
|
|
||||||
|
|
||||||
<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-status" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const StatusDemo = () => {
|
|
||||||
const statuses = [
|
|
||||||
{ label: 'Active', class: 'badge-success' },
|
|
||||||
{ label: 'Pending', class: 'badge-warning' },
|
|
||||||
{ label: 'Completed', class: 'badge-info' },
|
|
||||||
{ label: 'Failed', class: 'badge-error' },
|
|
||||||
{ label: 'Archived', class: 'badge-ghost' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Order Status'),
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, statuses.map(status =>
|
|
||||||
Badge({ class: status.class }, status.label)
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(StatusDemo, '#demo-status');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Count Badges
|
|
||||||
|
|
||||||
<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-count" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4 items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CountDemo = () => {
|
|
||||||
const notifications = $(3);
|
|
||||||
const messages = $(5);
|
|
||||||
const updates = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-6' }, [
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Span({}, 'Notifications'),
|
|
||||||
Badge({ class: 'badge-primary' }, () => notifications())
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Span({}, 'Messages'),
|
|
||||||
Badge({ class: 'badge-secondary' }, () => messages())
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Span({}, 'Updates'),
|
|
||||||
Badge({ class: 'badge-ghost' }, () => updates() || '0')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CountDemo, '#demo-count');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive Badge
|
|
||||||
|
|
||||||
<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-interactive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Div({ class: 'flex items-center gap-4' }, [
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => count(count() - 1) }, '-'),
|
|
||||||
Badge({ class: 'badge-primary text-lg min-w-[4rem] justify-center' }, () => count()),
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => count(count() + 1) }, '+')
|
|
||||||
]),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}, 'Reset')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-6' }, [
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-xs' }, 'XS'),
|
|
||||||
Badge({ class: 'badge-sm' }, 'SM'),
|
|
||||||
Badge({}, 'MD'),
|
|
||||||
Badge({ class: 'badge-lg' }, 'LG')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-primary badge-sm' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-secondary badge-sm' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-accent badge-sm' }, 'Accent')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-outline badge-primary' }, 'Outline'),
|
|
||||||
Badge({ class: 'badge-ghost badge-primary' }, 'Ghost')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Inline with Text
|
|
||||||
|
|
||||||
<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-inline" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const InlineDemo = () => {
|
|
||||||
return Div({ class: 'space-y-2' }, [
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
'Your order is ',
|
|
||||||
Badge({ class: 'badge-success badge-sm' }, 'Confirmed'),
|
|
||||||
' and will be shipped soon.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
'This feature is ',
|
|
||||||
Badge({ class: 'badge-warning badge-sm' }, 'Beta'),
|
|
||||||
' and may change.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
'Version ',
|
|
||||||
Badge({ class: 'badge-info badge-xs' }, 'v2.1.0'),
|
|
||||||
' released on March 2026'
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InlineDemo, '#demo-inline');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initBadgeExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Badge
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({}, 'Default'),
|
|
||||||
Badge({ class: 'badge-primary' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-secondary' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-accent' }, 'Accent'),
|
|
||||||
Badge({ class: 'badge-info' }, 'Info'),
|
|
||||||
Badge({ class: 'badge-success' }, 'Success'),
|
|
||||||
Badge({ class: 'badge-warning' }, 'Warning'),
|
|
||||||
Badge({ class: 'badge-error' }, 'Error')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Badge Sizes
|
|
||||||
const sizesTarget = document.querySelector('#demo-sizes');
|
|
||||||
if (sizesTarget && !sizesTarget.hasChildNodes()) {
|
|
||||||
const SizesDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 items-center' }, [
|
|
||||||
Badge({ class: 'badge-xs' }, 'Extra Small'),
|
|
||||||
Badge({ class: 'badge-sm' }, 'Small'),
|
|
||||||
Badge({}, 'Default'),
|
|
||||||
Badge({ class: 'badge-md' }, 'Medium'),
|
|
||||||
Badge({ class: 'badge-lg' }, 'Large')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SizesDemo, sizesTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Outline Badges
|
|
||||||
const outlineTarget = document.querySelector('#demo-outline');
|
|
||||||
if (outlineTarget && !outlineTarget.hasChildNodes()) {
|
|
||||||
const OutlineDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-outline' }, 'Default'),
|
|
||||||
Badge({ class: 'badge-outline badge-primary' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-outline badge-secondary' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-outline badge-accent' }, 'Accent'),
|
|
||||||
Badge({ class: 'badge-outline badge-info' }, 'Info'),
|
|
||||||
Badge({ class: 'badge-outline badge-success' }, 'Success'),
|
|
||||||
Badge({ class: 'badge-outline badge-warning' }, 'Warning'),
|
|
||||||
Badge({ class: 'badge-outline badge-error' }, 'Error')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(OutlineDemo, outlineTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Ghost Badges
|
|
||||||
const ghostTarget = document.querySelector('#demo-ghost');
|
|
||||||
if (ghostTarget && !ghostTarget.hasChildNodes()) {
|
|
||||||
const GhostDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-ghost' }, 'Default'),
|
|
||||||
Badge({ class: 'badge-ghost badge-primary' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-ghost badge-secondary' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-ghost badge-accent' }, 'Accent'),
|
|
||||||
Badge({ class: 'badge-ghost badge-info' }, 'Info'),
|
|
||||||
Badge({ class: 'badge-ghost badge-success' }, 'Success'),
|
|
||||||
Badge({ class: 'badge-ghost badge-warning' }, 'Warning'),
|
|
||||||
Badge({ class: 'badge-ghost badge-error' }, 'Error')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(GhostDemo, ghostTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. With Icons
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'gap-1' }, [
|
|
||||||
Img({src: Icons.iconSuccess}),
|
|
||||||
Span({}, 'Success')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1 badge-warning' }, [
|
|
||||||
Img({src: Icons.iconWarning}),
|
|
||||||
Span({}, 'Warning')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1 badge-error' }, [
|
|
||||||
Img({src: Icons.iconError}),
|
|
||||||
Span({}, 'Error')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1 badge-info' }, [
|
|
||||||
Img({src: Icons.iconInfo}),
|
|
||||||
Span({}, 'Info')
|
|
||||||
]),
|
|
||||||
Badge({ class: 'gap-1' }, [
|
|
||||||
Span({}, '★'),
|
|
||||||
Span({}, '4.5')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Status Badges
|
|
||||||
const statusTarget = document.querySelector('#demo-status');
|
|
||||||
if (statusTarget && !statusTarget.hasChildNodes()) {
|
|
||||||
const StatusDemo = () => {
|
|
||||||
const statuses = [
|
|
||||||
{ label: 'Active', class: 'badge-success' },
|
|
||||||
{ label: 'Pending', class: 'badge-warning' },
|
|
||||||
{ label: 'Completed', class: 'badge-info' },
|
|
||||||
{ label: 'Failed', class: 'badge-error' },
|
|
||||||
{ label: 'Archived', class: 'badge-ghost' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Order Status'),
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, statuses.map(status =>
|
|
||||||
Badge({ class: status.class }, status.label)
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(StatusDemo, statusTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Count Badges
|
|
||||||
const countTarget = document.querySelector('#demo-count');
|
|
||||||
if (countTarget && !countTarget.hasChildNodes()) {
|
|
||||||
const CountDemo = () => {
|
|
||||||
const notifications = $(3);
|
|
||||||
const messages = $(5);
|
|
||||||
const updates = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-6' }, [
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Span({}, 'Notifications'),
|
|
||||||
Badge({ class: 'badge-primary' }, () => notifications())
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Span({}, 'Messages'),
|
|
||||||
Badge({ class: 'badge-secondary' }, () => messages())
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Span({}, 'Updates'),
|
|
||||||
Badge({ class: 'badge-ghost' }, () => updates() || '0')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CountDemo, countTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Interactive Badge
|
|
||||||
const interactiveTarget = document.querySelector('#demo-interactive');
|
|
||||||
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Div({ class: 'flex items-center gap-4' }, [
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => count(count() - 1) }, '-'),
|
|
||||||
Badge({ class: 'badge-primary text-lg min-w-[4rem] justify-center' }, () => count()),
|
|
||||||
Button({ class: 'btn btn-sm', onclick: () => count(count() + 1) }, '+')
|
|
||||||
]),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}, 'Reset')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, interactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-xs' }, 'XS'),
|
|
||||||
Badge({ class: 'badge-sm' }, 'SM'),
|
|
||||||
Badge({}, 'MD'),
|
|
||||||
Badge({ class: 'badge-lg' }, 'LG')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-primary badge-sm' }, 'Primary'),
|
|
||||||
Badge({ class: 'badge-secondary badge-sm' }, 'Secondary'),
|
|
||||||
Badge({ class: 'badge-accent badge-sm' }, 'Accent')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-wrap gap-2' }, [
|
|
||||||
Badge({ class: 'badge-outline badge-primary' }, 'Outline'),
|
|
||||||
Badge({ class: 'badge-ghost badge-primary' }, 'Ghost')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10. Inline with Text
|
|
||||||
const inlineTarget = document.querySelector('#demo-inline');
|
|
||||||
if (inlineTarget && !inlineTarget.hasChildNodes()) {
|
|
||||||
const InlineDemo = () => {
|
|
||||||
return Div({ class: 'space-y-2' }, [
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
'Your order is ',
|
|
||||||
Badge({ class: 'badge-success badge-sm' }, 'Confirmed'),
|
|
||||||
' and will be shipped soon.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
'This feature is ',
|
|
||||||
Badge({ class: 'badge-warning badge-sm' }, 'Beta'),
|
|
||||||
' and may change.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
'Version ',
|
|
||||||
Badge({ class: 'badge-info badge-xs' }, 'v2.1.0'),
|
|
||||||
' released on March 2026'
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InlineDemo, inlineTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initBadgeExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initBadgeExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
# Button
|
|
||||||
|
|
||||||
Styled button with full DaisyUI support and reactive states.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Button`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :------------------ | :------------------------------------------ |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
|
||||||
| `loading` | `boolean \| Signal<boolean>` | `false` | Shows loading spinner |
|
|
||||||
| `badge` | `string \| Signal<string>` | `-` | Badge text displayed on corner |
|
|
||||||
| `badgeClass` | `string` | `'badge-secondary'` | Badge styling classes |
|
|
||||||
| `tooltip` | `string \| Signal<string>` | `-` | Tooltip text on hover |
|
|
||||||
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed before text |
|
|
||||||
| `onclick` | `function` | `-` | Click event handler |
|
|
||||||
| `type` | `string` | `'button'` | Native button type |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Button
|
|
||||||
|
|
||||||
<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 = () => {
|
|
||||||
return Button({ class: "btn btn-primary" }, "Click Me");
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, "#demo-basic");
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Loading 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-loading" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const LoadingDemo = () => {
|
|
||||||
const isSaving = $(false);
|
|
||||||
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn btn-success",
|
|
||||||
loading: isSaving,
|
|
||||||
onclick: async () => {
|
|
||||||
isSaving(true);
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
||||||
isSaving(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"Save Changes",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$mount(LoadingDemo, "#demo-loading");
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Badge
|
|
||||||
|
|
||||||
<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-badge" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BadgeDemo = () => {
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn btn-outline",
|
|
||||||
badge: "3",
|
|
||||||
badgeClass: "badge-accent",
|
|
||||||
},
|
|
||||||
"Notifications",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$mount(BadgeDemo, "#demo-badge");
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 = () => {
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn btn-ghost",
|
|
||||||
tooltip: "Delete item",
|
|
||||||
},
|
|
||||||
"Delete",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$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 items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DisabledDemo = () => {
|
|
||||||
const isDisabled = $(true);
|
|
||||||
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn btn-primary",
|
|
||||||
disabled: isDisabled,
|
|
||||||
},
|
|
||||||
"Submit",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: "flex flex-wrap gap-2 justify-center" }, [
|
|
||||||
Button({ class: "btn btn-primary" }, "Primary"),
|
|
||||||
Button({ class: "btn btn-secondary" }, "Secondary"),
|
|
||||||
Button({ class: "btn btn-accent" }, "Accent"),
|
|
||||||
Button({ class: "btn btn-ghost" }, "Ghost"),
|
|
||||||
Button({ class: "btn btn-outline" }, "Outline"),
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, "#demo-variants");
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initButtonExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Button
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => Button({ class: "btn btn-primary" }, "Click Me");
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Loading State
|
|
||||||
const loadingTarget = document.querySelector('#demo-loading');
|
|
||||||
if (loadingTarget && !loadingTarget.hasChildNodes()) {
|
|
||||||
const LoadingDemo = () => {
|
|
||||||
const isSaving = $(false);
|
|
||||||
return Button({
|
|
||||||
class: "btn btn-success",
|
|
||||||
loading: isSaving,
|
|
||||||
onclick: async () => {
|
|
||||||
isSaving(true);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
isSaving(false);
|
|
||||||
}
|
|
||||||
}, "Save Changes");
|
|
||||||
};
|
|
||||||
$mount(LoadingDemo, loadingTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Badge
|
|
||||||
const badgeTarget = document.querySelector('#demo-badge');
|
|
||||||
if (badgeTarget && !badgeTarget.hasChildNodes()) {
|
|
||||||
const BadgeDemo = () => Button({
|
|
||||||
class: "btn btn-outline",
|
|
||||||
badge: "3",
|
|
||||||
badgeClass: "badge-accent"
|
|
||||||
}, "Notifications");
|
|
||||||
$mount(BadgeDemo, badgeTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Tooltip
|
|
||||||
const tooltipTarget = document.querySelector('#demo-tooltip');
|
|
||||||
if (tooltipTarget && !tooltipTarget.hasChildNodes()) {
|
|
||||||
const TooltipDemo = () => Button({
|
|
||||||
class: "btn btn-ghost",
|
|
||||||
tooltip: "Delete item"
|
|
||||||
}, "Delete");
|
|
||||||
$mount(TooltipDemo, tooltipTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Disabled State
|
|
||||||
const disabledTarget = document.querySelector('#demo-disabled');
|
|
||||||
if (disabledTarget && !disabledTarget.hasChildNodes()) {
|
|
||||||
const DisabledDemo = () => {
|
|
||||||
const isDisabled = $(true);
|
|
||||||
return Button({
|
|
||||||
class: "btn btn-primary",
|
|
||||||
disabled: isDisabled
|
|
||||||
}, "Submit");
|
|
||||||
};
|
|
||||||
$mount(DisabledDemo, disabledTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => Div({ class: "flex flex-wrap gap-2 justify-center" }, [
|
|
||||||
Button({ class: "btn btn-primary" }, "Primary"),
|
|
||||||
Button({ class: "btn btn-secondary" }, "Secondary"),
|
|
||||||
Button({ class: "btn btn-accent" }, "Accent"),
|
|
||||||
Button({ class: "btn btn-ghost" }, "Ghost"),
|
|
||||||
Button({ class: "btn btn-outline" }, "Outline")
|
|
||||||
]);
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ejecutar la función después de definirla
|
|
||||||
initButtonExamples();
|
|
||||||
|
|
||||||
// Registrar para navegación en Docsify
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initButtonExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
> # Button(props, children?: string | Signal | [...]): HTMLElement
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :------------------ | :------------------------------- |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (daisyUI) |
|
|
||||||
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
|
|
||||||
| `loading` | `boolean \| Signal<boolean>` | `false` | Shows loading spinner |
|
|
||||||
| `badge` | `string \| Signal<string>` | `-` | Badge text displayed on corner |
|
|
||||||
| `badgeClass` | `string` | `'badge-secondary'` | Badge styling classes |
|
|
||||||
| `tooltip` | `string \| Signal<string>` | `-` | Tooltip text on hover |
|
|
||||||
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed before text |
|
|
||||||
| `onclick` | `function` | `-` | Click event handler |
|
|
||||||
| `type` | `string` | `'button'` | Native button type |
|
|
||||||
|
|
||||||
## Class Options
|
|
||||||
|
|
||||||
For more detailed information about the underlying styling system, visit the daisyUI documentation:
|
|
||||||
|
|
||||||
- [daisyUI Button](https://daisyui.com/components/button)
|
|
||||||
|
|
||||||
| Class Name | Category | Description |
|
|
||||||
| :-------------- | :------------ | :------------------------------------ |
|
|
||||||
| `btn-neutral` | `Color` 🎨 | Neutral brand color |
|
|
||||||
| `btn-primary` | `Color` 🎨 | Primary brand color |
|
|
||||||
| `btn-secondary` | `Color` 🎨 | Secondary brand color |
|
|
||||||
| `btn-accent` | `Color` 🎨 | Accent brand color |
|
|
||||||
| `btn-info` | `Color` 🎨 | Informational blue color |
|
|
||||||
| `btn-success` | `Color` 🎨 | Success green color |
|
|
||||||
| `btn-warning` | `Color` 🎨 | Warning yellow color |
|
|
||||||
| `btn-error` | `Color` 🎨 | Error red color |
|
|
||||||
| `btn-xl` | `Size` 📏 | Extra large scale |
|
|
||||||
| `btn-lg` | `Size` 📏 | Large scale |
|
|
||||||
| `btn-md` | `Size` 📏 | Medium scale (Default) |
|
|
||||||
| `btn-sm` | `Size` 📏 | Small scale |
|
|
||||||
| `btn-xs` | `Size` 📏 | Extra small scale |
|
|
||||||
| `btn-outline` | `Style` ✨ | Transparent with colored border |
|
|
||||||
| `btn-dash` | `Style` ✨ | Dashed border style |
|
|
||||||
| `btn-soft` | `Style` ✨ | Low opacity background color |
|
|
||||||
| `btn-ghost` | `Style` ✨ | No background, hover effect only |
|
|
||||||
| `btn-link` | `Style` ✨ | Looks like a text link |
|
|
||||||
| `btn-square` | `Shape` 📐 | 1:1 aspect ratio |
|
|
||||||
| `btn-circle` | `Shape` 📐 | 1:1 aspect ratio with rounded corners |
|
|
||||||
| `btn-wide` | `Shape` 📐 | Extra horizontal padding |
|
|
||||||
| `btn-block` | `Shape` 📐 | Full width of container |
|
|
||||||
| `btn-active` | `Behavior` ⚙️ | Forced active/pressed state |
|
|
||||||
| `btn-disabled` | `Behavior` ⚙️ | Visual and functional disabled state |
|
|
||||||
|
|
||||||
### Basic Button
|
|
||||||
|
|
||||||
<div id="demo-basic" class="card bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Button({ class: "btn-primary" }, "Click Me");
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, "#demo-basic");
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Loading State
|
|
||||||
|
|
||||||
<div id="demo-loading" class="card bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const LoadingDemo = () => {
|
|
||||||
const isSaving = $(false);
|
|
||||||
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn-success",
|
|
||||||
loading: isSaving,
|
|
||||||
onclick: async () => {
|
|
||||||
isSaving(true);
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
||||||
isSaving(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"Save Changes",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$mount(LoadingDemo, "#demo-loading");
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Badge
|
|
||||||
|
|
||||||
<div id="demo-badge" class="card bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BadgeDemo = () => {
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn-outline",
|
|
||||||
badge: "3",
|
|
||||||
badgeClass: "badge-accent",
|
|
||||||
},
|
|
||||||
"Notifications",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$mount(BadgeDemo, "#demo-badge");
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Tooltip
|
|
||||||
|
|
||||||
<div id="demo-tooltip" class="card bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const TooltipDemo = () => {
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn-ghost",
|
|
||||||
tooltip: "Delete item",
|
|
||||||
},
|
|
||||||
"Delete",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$mount(TooltipDemo, "#demo-tooltip");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Disabled State
|
|
||||||
|
|
||||||
<div id="demo-disabled" class="card bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DisabledDemo = () => {
|
|
||||||
const isDisabled = $(true);
|
|
||||||
|
|
||||||
return Button(
|
|
||||||
{
|
|
||||||
class: "btn-primary btn-disabled",
|
|
||||||
},
|
|
||||||
"Submit",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
$mount(DisabledDemo, "#demo-disabled");
|
|
||||||
```
|
|
||||||
|
|
||||||
### All Variants
|
|
||||||
|
|
||||||
<div id="demo-variants" class="card bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: "flex flex-wrap gap-2 justify-center" }, [
|
|
||||||
Button({ class: "btn-primary" }, "Primary"),
|
|
||||||
Button({ class: "btn-secondary" }, "Secondary"),
|
|
||||||
Button({ class: "btn-accent" }, "Accent"),
|
|
||||||
Button({ class: "btn-ghost" }, "Ghost"),
|
|
||||||
Button({ class: "btn-outline" }, "Outline"),
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, "#demo-variants");
|
|
||||||
```
|
|
||||||
|
|
||||||
<div id="demo-test" class="card bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const TestDemo = () => {
|
|
||||||
return Div({ class: "flex flex-wrap gap-2 justify-center" }, [
|
|
||||||
$html("span", {class: "indicator"},[
|
|
||||||
5,
|
|
||||||
Button('Click')])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(TestDemo, "#demo-test");
|
|
||||||
```
|
|
||||||
@@ -1,479 +0,0 @@
|
|||||||
# 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>
|
|
||||||
@@ -1,382 +0,0 @@
|
|||||||
# 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>
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
# 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>
|
|
||||||
@@ -1,918 +0,0 @@
|
|||||||
# Drawer
|
|
||||||
|
|
||||||
Drawer component for creating off-canvas side panels with overlay and toggle functionality.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Drawer`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `id` | `string` | Required | Unique identifier for the drawer |
|
|
||||||
| `open` | `boolean \| Signal<boolean>` | `false` | Drawer open state |
|
|
||||||
| `side` | `VNode` | Required | Content to display in the drawer panel |
|
|
||||||
| `content` | `VNode` | Required | Main page content |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Drawer
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'basic-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'text-lg font-bold mb-4' }, 'Menu'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Home'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'About'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4 text-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Drawer')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Navigation Drawer
|
|
||||||
|
|
||||||
<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-nav" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const NavDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const activePage = $('home');
|
|
||||||
|
|
||||||
const pages = {
|
|
||||||
home: 'Welcome to the Home Page!',
|
|
||||||
about: 'About Us - Learn more about our company',
|
|
||||||
services: 'Our Services - What we offer',
|
|
||||||
contact: 'Contact Us - Get in touch'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'nav-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4 w-64' }, [
|
|
||||||
Div({ class: 'text-xl font-bold mb-6' }, 'MyApp'),
|
|
||||||
Div({ class: 'flex flex-col gap-1' }, [
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'home' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('home');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, '🏠 Home'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'about' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('about');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, 'ℹ️ About'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'services' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('services');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, '⚙️ Services'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'contact' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('contact');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, '📧 Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, '☰'),
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'card bg-base-200 shadow-lg' }, [
|
|
||||||
Div({ class: 'card-body' }, [
|
|
||||||
Div({ class: 'text-2xl font-bold mb-2' }, () => activePage().charAt(0).toUpperCase() + activePage().slice(1)),
|
|
||||||
Div({ class: 'text-lg' }, () => pages[activePage()])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(NavDrawer, '#demo-nav');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Settings Drawer
|
|
||||||
|
|
||||||
<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-settings" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const SettingsDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const darkMode = $(false);
|
|
||||||
const notifications = $(true);
|
|
||||||
const autoSave = $(false);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'settings-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4 w-80' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-6' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Settings'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle btn-sm',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, '✕')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Dark Mode'),
|
|
||||||
Swap({
|
|
||||||
value: darkMode,
|
|
||||||
on: "🌙",
|
|
||||||
off: "☀️",
|
|
||||||
onclick: () => darkMode(!darkMode())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Notifications'),
|
|
||||||
Swap({
|
|
||||||
value: notifications,
|
|
||||||
on: "🔔",
|
|
||||||
off: "🔕",
|
|
||||||
onclick: () => notifications(!notifications())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Auto Save'),
|
|
||||||
Swap({
|
|
||||||
value: autoSave,
|
|
||||||
on: "✅",
|
|
||||||
off: "⭕",
|
|
||||||
onclick: () => autoSave(!autoSave())
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'divider my-4' }),
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary flex-1',
|
|
||||||
onclick: () => {
|
|
||||||
isOpen(false);
|
|
||||||
Toast('Settings saved!', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
}, 'Save'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost flex-1',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'Dashboard'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, '⚙️')
|
|
||||||
]),
|
|
||||||
Div({ class: 'mt-4 grid grid-cols-2 gap-4' }, [
|
|
||||||
Div({ class: 'stat bg-base-200 rounded-lg p-4' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Users'),
|
|
||||||
Div({ class: 'stat-value' }, '1,234')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-200 rounded-lg p-4' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Revenue'),
|
|
||||||
Div({ class: 'stat-value' }, '$45K')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(SettingsDrawer, '#demo-settings');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cart Drawer
|
|
||||||
|
|
||||||
<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-cart" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CartDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const cart = $([
|
|
||||||
{ id: 1, name: 'Product 1', price: 29, quantity: 2 },
|
|
||||||
{ id: 2, name: 'Product 2', price: 49, quantity: 1 }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const updateQuantity = (id, delta) => {
|
|
||||||
cart(cart().map(item => {
|
|
||||||
if (item.id === id) {
|
|
||||||
const newQty = Math.max(0, item.quantity + delta);
|
|
||||||
return newQty === 0 ? null : { ...item, quantity: newQty };
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}).filter(Boolean));
|
|
||||||
};
|
|
||||||
|
|
||||||
const total = () => cart().reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'cart-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'flex flex-col h-full' }, [
|
|
||||||
Div({ class: 'p-4 border-b border-base-300' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, `Cart (${cart().length} items)`),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle btn-sm',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, '✕')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1 overflow-y-auto p-4' }, cart().length === 0
|
|
||||||
? Div({ class: 'text-center text-gray-500 mt-8' }, 'Your cart is empty')
|
|
||||||
: Div({ class: 'flex flex-col gap-3' }, cart().map(item =>
|
|
||||||
Div({ class: 'flex gap-3 items-center p-2 bg-base-200 rounded-lg' }, [
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.name),
|
|
||||||
Div({ class: 'text-sm' }, `$${item.price} each`)
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-circle',
|
|
||||||
onclick: () => updateQuantity(item.id, -1)
|
|
||||||
}, '-'),
|
|
||||||
Span({ class: 'w-8 text-center' }, item.quantity),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-circle',
|
|
||||||
onclick: () => updateQuantity(item.id, 1)
|
|
||||||
}, '+')
|
|
||||||
]),
|
|
||||||
Span({ class: 'font-bold w-16 text-right' }, `$${item.price * item.quantity}`)
|
|
||||||
])
|
|
||||||
))
|
|
||||||
),
|
|
||||||
Div({ class: 'p-4 border-t border-base-300' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-4' }, [
|
|
||||||
Span({ class: 'font-bold' }, 'Total'),
|
|
||||||
Span({ class: 'text-xl font-bold' }, () => `$${total()}`)
|
|
||||||
]),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary w-full',
|
|
||||||
onclick: () => {
|
|
||||||
isOpen(false);
|
|
||||||
Toast('Checkout initiated!', 'alert-success', 2000);
|
|
||||||
},
|
|
||||||
disabled: () => cart().length === 0
|
|
||||||
}, 'Checkout')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'Store'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, () => `🛒 Cart (${cart().length})`)
|
|
||||||
]),
|
|
||||||
Div({ class: 'mt-4 grid grid-cols-2 gap-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-outline h-32 flex flex-col',
|
|
||||||
onclick: () => {
|
|
||||||
cart([...cart(), { id: Date.now(), name: 'New Product', price: 39, quantity: 1 }]);
|
|
||||||
Toast('Added to cart!', 'alert-success', 1500);
|
|
||||||
}
|
|
||||||
}, ['📦', 'Add to Cart'])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CartDrawer, '#demo-cart');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Responsive Drawer
|
|
||||||
|
|
||||||
<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-responsive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ResponsiveDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const activePage = $('home');
|
|
||||||
|
|
||||||
const MenuItems = () => Div({ class: 'flex flex-col gap-1 p-4' }, [
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'home' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('home');
|
|
||||||
if (window.innerWidth < 1024) isOpen(false);
|
|
||||||
}
|
|
||||||
}, '🏠 Home'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'analytics' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('analytics');
|
|
||||||
if (window.innerWidth < 1024) isOpen(false);
|
|
||||||
}
|
|
||||||
}, '📊 Analytics'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'settings' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('settings');
|
|
||||||
if (window.innerWidth < 1024) isOpen(false);
|
|
||||||
}
|
|
||||||
}, '⚙️ Settings')
|
|
||||||
]);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'responsive-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'w-64' }, [
|
|
||||||
Div({ class: 'text-xl font-bold p-4 border-b border-base-300' }, 'Menu'),
|
|
||||||
MenuItems()
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'flex' }, [
|
|
||||||
Div({ class: 'hidden lg:block w-64 border-r border-base-300' }, [MenuItems()]),
|
|
||||||
Div({ class: 'flex-1 p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center lg:hidden mb-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, '☰'),
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'card bg-base-200' }, [
|
|
||||||
Div({ class: 'card-body' }, [
|
|
||||||
Div({ class: 'text-2xl font-bold' }, () => activePage().charAt(0).toUpperCase() + activePage().slice(1)),
|
|
||||||
Div({}, 'Content area. On desktop, the menu is always visible on the left.')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ResponsiveDrawer, '#demo-responsive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Form Drawer
|
|
||||||
|
|
||||||
<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 FormDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const message = $('');
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (name() && email() && message()) {
|
|
||||||
Toast('Message sent!', 'alert-success', 2000);
|
|
||||||
isOpen(false);
|
|
||||||
name('');
|
|
||||||
email('');
|
|
||||||
message('');
|
|
||||||
} else {
|
|
||||||
Toast('Please fill all fields', 'alert-warning', 2000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'form-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4 w-96' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-4' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Contact Us'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle btn-sm',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, '✕')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Name',
|
|
||||||
value: name,
|
|
||||||
placeholder: 'Your name',
|
|
||||||
oninput: (e) => name(e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'your@email.com',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'form-control' }, [
|
|
||||||
Span({ class: 'label-text mb-1' }, 'Message'),
|
|
||||||
$html('textarea', {
|
|
||||||
class: 'textarea textarea-bordered h-24',
|
|
||||||
placeholder: 'Your message',
|
|
||||||
value: message,
|
|
||||||
oninput: (e) => message(e.target.value)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2 mt-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary flex-1',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Send'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost flex-1',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4 text-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Contact Us')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(FormDrawer, '#demo-form');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initDrawerExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Drawer
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'basic-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'text-lg font-bold mb-4' }, 'Menu'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Home'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'About'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4 text-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Drawer')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Navigation Drawer
|
|
||||||
const navTarget = document.querySelector('#demo-nav');
|
|
||||||
if (navTarget && !navTarget.hasChildNodes()) {
|
|
||||||
const NavDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const activePage = $('home');
|
|
||||||
|
|
||||||
const pages = {
|
|
||||||
home: 'Welcome to the Home Page!',
|
|
||||||
about: 'About Us - Learn more about our company',
|
|
||||||
services: 'Our Services - What we offer',
|
|
||||||
contact: 'Contact Us - Get in touch'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'nav-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4 w-64' }, [
|
|
||||||
Div({ class: 'text-xl font-bold mb-6' }, 'MyApp'),
|
|
||||||
Div({ class: 'flex flex-col gap-1' }, [
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'home' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('home');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, '🏠 Home'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'about' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('about');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, 'ℹ️ About'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'services' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('services');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, '⚙️ Services'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'contact' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('contact');
|
|
||||||
isOpen(false);
|
|
||||||
}
|
|
||||||
}, '📧 Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, '☰'),
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'card bg-base-200 shadow-lg' }, [
|
|
||||||
Div({ class: 'card-body' }, [
|
|
||||||
Div({ class: 'text-2xl font-bold mb-2' }, () => activePage().charAt(0).toUpperCase() + activePage().slice(1)),
|
|
||||||
Div({ class: 'text-lg' }, () => pages[activePage()])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(NavDrawer, navTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Settings Drawer
|
|
||||||
const settingsTarget = document.querySelector('#demo-settings');
|
|
||||||
if (settingsTarget && !settingsTarget.hasChildNodes()) {
|
|
||||||
const SettingsDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const darkMode = $(false);
|
|
||||||
const notifications = $(true);
|
|
||||||
const autoSave = $(false);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'settings-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4 w-80' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-6' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Settings'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle btn-sm',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, '✕')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Dark Mode'),
|
|
||||||
Swap({
|
|
||||||
value: darkMode,
|
|
||||||
on: "🌙",
|
|
||||||
off: "☀️",
|
|
||||||
onclick: () => darkMode(!darkMode())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Notifications'),
|
|
||||||
Swap({
|
|
||||||
value: notifications,
|
|
||||||
on: "🔔",
|
|
||||||
off: "🔕",
|
|
||||||
onclick: () => notifications(!notifications())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Auto Save'),
|
|
||||||
Swap({
|
|
||||||
value: autoSave,
|
|
||||||
on: "✅",
|
|
||||||
off: "⭕",
|
|
||||||
onclick: () => autoSave(!autoSave())
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'divider my-4' }),
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary flex-1',
|
|
||||||
onclick: () => {
|
|
||||||
isOpen(false);
|
|
||||||
Toast('Settings saved!', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
}, 'Save'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost flex-1',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'Dashboard'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, '⚙️')
|
|
||||||
]),
|
|
||||||
Div({ class: 'mt-4 grid grid-cols-2 gap-4' }, [
|
|
||||||
Div({ class: 'stat bg-base-200 rounded-lg p-4' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Users'),
|
|
||||||
Div({ class: 'stat-value' }, '1,234')
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat bg-base-200 rounded-lg p-4' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Revenue'),
|
|
||||||
Div({ class: 'stat-value' }, '$45K')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(SettingsDrawer, settingsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Cart Drawer
|
|
||||||
const cartTarget = document.querySelector('#demo-cart');
|
|
||||||
if (cartTarget && !cartTarget.hasChildNodes()) {
|
|
||||||
const CartDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const cart = $([
|
|
||||||
{ id: 1, name: 'Product 1', price: 29, quantity: 2 },
|
|
||||||
{ id: 2, name: 'Product 2', price: 49, quantity: 1 }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const updateQuantity = (id, delta) => {
|
|
||||||
cart(cart().map(item => {
|
|
||||||
if (item.id === id) {
|
|
||||||
const newQty = Math.max(0, item.quantity + delta);
|
|
||||||
return newQty === 0 ? null : { ...item, quantity: newQty };
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
}).filter(Boolean));
|
|
||||||
};
|
|
||||||
|
|
||||||
const total = () => cart().reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'cart-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'flex flex-col h-full' }, [
|
|
||||||
Div({ class: 'p-4 border-b border-base-300' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, `Cart (${cart().length} items)`),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle btn-sm',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, '✕')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1 overflow-y-auto p-4' }, cart().length === 0
|
|
||||||
? Div({ class: 'text-center text-gray-500 mt-8' }, 'Your cart is empty')
|
|
||||||
: Div({ class: 'flex flex-col gap-3' }, cart().map(item =>
|
|
||||||
Div({ class: 'flex gap-3 items-center p-2 bg-base-200 rounded-lg' }, [
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.name),
|
|
||||||
Div({ class: 'text-sm' }, `$${item.price} each`)
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-circle',
|
|
||||||
onclick: () => updateQuantity(item.id, -1)
|
|
||||||
}, '-'),
|
|
||||||
Span({ class: 'w-8 text-center' }, item.quantity),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-circle',
|
|
||||||
onclick: () => updateQuantity(item.id, 1)
|
|
||||||
}, '+')
|
|
||||||
]),
|
|
||||||
Span({ class: 'font-bold w-16 text-right' }, `$${item.price * item.quantity}`)
|
|
||||||
])
|
|
||||||
))
|
|
||||||
),
|
|
||||||
Div({ class: 'p-4 border-t border-base-300' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-4' }, [
|
|
||||||
Span({ class: 'font-bold' }, 'Total'),
|
|
||||||
Span({ class: 'text-xl font-bold' }, () => `$${total()}`)
|
|
||||||
]),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary w-full',
|
|
||||||
onclick: () => {
|
|
||||||
isOpen(false);
|
|
||||||
Toast('Checkout initiated!', 'alert-success', 2000);
|
|
||||||
},
|
|
||||||
disabled: () => cart().length === 0
|
|
||||||
}, 'Checkout')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'Store'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, () => `🛒 Cart (${cart().length})`)
|
|
||||||
]),
|
|
||||||
Div({ class: 'mt-4 grid grid-cols-2 gap-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-outline h-32 flex flex-col',
|
|
||||||
onclick: () => {
|
|
||||||
cart([...cart(), { id: Date.now(), name: 'New Product', price: 39, quantity: 1 }]);
|
|
||||||
Toast('Added to cart!', 'alert-success', 1500);
|
|
||||||
}
|
|
||||||
}, ['📦', 'Add to Cart'])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CartDrawer, cartTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Responsive Drawer
|
|
||||||
const responsiveTarget = document.querySelector('#demo-responsive');
|
|
||||||
if (responsiveTarget && !responsiveTarget.hasChildNodes()) {
|
|
||||||
const ResponsiveDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const activePage = $('home');
|
|
||||||
|
|
||||||
const MenuItems = () => Div({ class: 'flex flex-col gap-1 p-4' }, [
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'home' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('home');
|
|
||||||
if (window.innerWidth < 1024) isOpen(false);
|
|
||||||
}
|
|
||||||
}, '🏠 Home'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'analytics' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('analytics');
|
|
||||||
if (window.innerWidth < 1024) isOpen(false);
|
|
||||||
}
|
|
||||||
}, '📊 Analytics'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost justify-start ${activePage() === 'settings' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => {
|
|
||||||
activePage('settings');
|
|
||||||
if (window.innerWidth < 1024) isOpen(false);
|
|
||||||
}
|
|
||||||
}, '⚙️ Settings')
|
|
||||||
]);
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'responsive-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'w-64' }, [
|
|
||||||
Div({ class: 'text-xl font-bold p-4 border-b border-base-300' }, 'Menu'),
|
|
||||||
MenuItems()
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'flex' }, [
|
|
||||||
Div({ class: 'hidden lg:block w-64 border-r border-base-300' }, [MenuItems()]),
|
|
||||||
Div({ class: 'flex-1 p-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center lg:hidden mb-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, '☰'),
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'card bg-base-200' }, [
|
|
||||||
Div({ class: 'card-body' }, [
|
|
||||||
Div({ class: 'text-2xl font-bold' }, () => activePage().charAt(0).toUpperCase() + activePage().slice(1)),
|
|
||||||
Div({}, 'Content area. On desktop, the menu is always visible on the left.')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ResponsiveDrawer, responsiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Form Drawer
|
|
||||||
const formTarget = document.querySelector('#demo-form');
|
|
||||||
if (formTarget && !formTarget.hasChildNodes()) {
|
|
||||||
const FormDrawer = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const message = $('');
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (name() && email() && message()) {
|
|
||||||
Toast('Message sent!', 'alert-success', 2000);
|
|
||||||
isOpen(false);
|
|
||||||
name('');
|
|
||||||
email('');
|
|
||||||
message('');
|
|
||||||
} else {
|
|
||||||
Toast('Please fill all fields', 'alert-warning', 2000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Drawer({
|
|
||||||
id: 'form-drawer',
|
|
||||||
open: isOpen,
|
|
||||||
side: Div({ class: 'p-4 w-96' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center mb-4' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Contact Us'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle btn-sm',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, '✕')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Name',
|
|
||||||
value: name,
|
|
||||||
placeholder: 'Your name',
|
|
||||||
oninput: (e) => name(e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'your@email.com',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'form-control' }, [
|
|
||||||
Span({ class: 'label-text mb-1' }, 'Message'),
|
|
||||||
$html('textarea', {
|
|
||||||
class: 'textarea textarea-bordered h-24',
|
|
||||||
placeholder: 'Your message',
|
|
||||||
value: message,
|
|
||||||
oninput: (e) => message(e.target.value)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2 mt-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary flex-1',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Send'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost flex-1',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
content: Div({ class: 'p-4 text-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Contact Us')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(FormDrawer, formTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initDrawerExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initDrawerExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,489 +0,0 @@
|
|||||||
# Dropdown
|
|
||||||
|
|
||||||
Dropdown component for creating menus, selectors, and action panels that appear when triggered. Supports both array-based items and custom content.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Dropdown`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `label` | `string \| VNode \| Signal` | `-` | Button label or content |
|
|
||||||
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed next to label |
|
|
||||||
| `items` | `Array<MenuItem> \| Signal<Array>` | `-` | Array of menu items (alternative to children) |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `VNode \| function` | `-` | Custom dropdown content (alternative to items) |
|
|
||||||
|
|
||||||
### MenuItem Structure (when using `items`)
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| :---------- | :--------------------------- | :----------------------------------------------- |
|
|
||||||
| `label` | `string \| VNode` | Menu item text |
|
|
||||||
| `icon` | `string \| VNode` | Optional icon for the menu item |
|
|
||||||
| `onclick` | `function` | Click handler |
|
|
||||||
| `class` | `string` | Additional CSS classes for the menu item |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Dropdown (Items Array)
|
|
||||||
|
|
||||||
<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 = () => {
|
|
||||||
return Dropdown({
|
|
||||||
label: 'Options',
|
|
||||||
items: [
|
|
||||||
{ label: 'Profile', onclick: () => Toast('Profile clicked', 'alert-info', 2000) },
|
|
||||||
{ label: 'Settings', onclick: () => Toast('Settings clicked', 'alert-info', 2000) },
|
|
||||||
{ label: 'Logout', onclick: () => Toast('Logged out', 'alert-warning', 2000), class: 'text-error' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Icons (Items Array)
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Dropdown({
|
|
||||||
label: 'Menu',
|
|
||||||
icon: '☰',
|
|
||||||
items: [
|
|
||||||
{ icon: '👤', label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) },
|
|
||||||
{ icon: '⭐', label: 'Favorites', onclick: () => Toast('Favorites', 'alert-info', 2000) },
|
|
||||||
{ icon: '📁', label: 'Documents', onclick: () => Toast('Documents', 'alert-info', 2000) },
|
|
||||||
{ icon: '⚙️', label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Action Dropdown (Items Array)
|
|
||||||
|
|
||||||
<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-actions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const handleAction = (action) => {
|
|
||||||
Toast(`${action} action`, 'alert-info', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: 'Actions',
|
|
||||||
class: 'dropdown-end',
|
|
||||||
items: [
|
|
||||||
{ icon: '✏️', label: 'Edit', onclick: () => handleAction('Edit') },
|
|
||||||
{ icon: '📋', label: 'Copy', onclick: () => handleAction('Copy') },
|
|
||||||
{ icon: '🗑️', label: 'Delete', onclick: () => handleAction('Delete'), class: 'text-error' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
|
||||||
```
|
|
||||||
|
|
||||||
### User Dropdown (Items Array)
|
|
||||||
|
|
||||||
<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-user" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const UserDropdown = () => {
|
|
||||||
return Dropdown({
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-full w-8 h-8 flex items-center justify-center text-sm' }, 'JD')
|
|
||||||
]),
|
|
||||||
'John Doe'
|
|
||||||
]),
|
|
||||||
class: 'dropdown-end',
|
|
||||||
items: [
|
|
||||||
{ label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) },
|
|
||||||
{ label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) },
|
|
||||||
{ label: 'Sign Out', onclick: () => Toast('Signed out', 'alert-warning', 2000), class: 'text-error' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(UserDropdown, '#demo-user');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Items
|
|
||||||
|
|
||||||
<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 ReactiveDropdown = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
const items = () => [
|
|
||||||
{ label: `Count: ${count()}`, onclick: () => {} },
|
|
||||||
{ label: 'Increment', onclick: () => count(count() + 1) },
|
|
||||||
{ label: 'Decrement', onclick: () => count(count() - 1) },
|
|
||||||
{ label: 'Reset', onclick: () => count(0) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: () => `Counter (${count()})`,
|
|
||||||
items: items
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ReactiveDropdown, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Notification Dropdown (Custom Children)
|
|
||||||
|
|
||||||
<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-notifications" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const NotificationsDropdown = () => {
|
|
||||||
const notifications = $([
|
|
||||||
{ id: 1, title: 'New message', time: '5 min ago', read: false },
|
|
||||||
{ id: 2, title: 'Update available', time: '1 hour ago', read: false },
|
|
||||||
{ id: 3, title: 'Task completed', time: '2 hours ago', read: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const markAsRead = (id) => {
|
|
||||||
notifications(notifications().map(n =>
|
|
||||||
n.id === id ? { ...n, read: true } : n
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
const unreadCount = () => notifications().filter(n => !n.read).length;
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: Span({ class: 'relative' }, [
|
|
||||||
'🔔',
|
|
||||||
() => unreadCount() > 0 ? Span({ class: 'badge badge-xs badge-error absolute -top-1 -right-2' }, unreadCount()) : null
|
|
||||||
]),
|
|
||||||
class: 'dropdown-end',
|
|
||||||
children: () => Div({ class: 'w-80' }, [
|
|
||||||
Div({ class: 'p-3 border-b border-base-300 font-bold' }, `Notifications (${unreadCount()} unread)`),
|
|
||||||
Div({ class: 'max-h-64 overflow-y-auto' }, notifications().map(notif =>
|
|
||||||
Div({
|
|
||||||
class: `p-3 border-b border-base-300 cursor-pointer hover:bg-base-200 ${!notif.read ? 'bg-primary/5' : ''}`,
|
|
||||||
onclick: () => markAsRead(notif.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'font-medium' }, notif.title),
|
|
||||||
Div({ class: 'text-xs opacity-60' }, notif.time),
|
|
||||||
!notif.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New')
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
Div({ class: 'p-2 text-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-ghost w-full',
|
|
||||||
onclick: () => notifications([])
|
|
||||||
}, 'Clear all')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(NotificationsDropdown, '#demo-notifications');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Content Dropdown
|
|
||||||
|
|
||||||
<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-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CustomDropdown = () => {
|
|
||||||
const selected = $('Option 1');
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: () => selected(),
|
|
||||||
children: () => Div({ class: 'p-4 min-w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Select an option'),
|
|
||||||
Div({ class: 'flex flex-col gap-1' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm justify-start',
|
|
||||||
onclick: () => selected('Option 1')
|
|
||||||
}, 'Option 1'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm justify-start',
|
|
||||||
onclick: () => selected('Option 2')
|
|
||||||
}, 'Option 2'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm justify-start',
|
|
||||||
onclick: () => selected('Option 3')
|
|
||||||
}, 'Option 3')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CustomDropdown, '#demo-custom');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const commonItems = [
|
|
||||||
{ label: 'Item 1', onclick: () => Toast('Item 1', 'alert-info', 2000) },
|
|
||||||
{ label: 'Item 2', onclick: () => Toast('Item 2', 'alert-info', 2000) },
|
|
||||||
{ label: 'Item 3', onclick: () => Toast('Item 3', 'alert-info', 2000) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Dropdown({ label: 'Default', items: commonItems }),
|
|
||||||
Dropdown({ label: 'With Icon', icon: '☰', items: commonItems }),
|
|
||||||
Dropdown({ label: 'End Position', class: 'dropdown-end', items: commonItems })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initDropdownExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Dropdown (Items Array)
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Dropdown({
|
|
||||||
label: 'Options',
|
|
||||||
items: [
|
|
||||||
{ label: 'Profile', onclick: () => Toast('Profile clicked', 'alert-info', 2000) },
|
|
||||||
{ label: 'Settings', onclick: () => Toast('Settings clicked', 'alert-info', 2000) },
|
|
||||||
{ label: 'Logout', onclick: () => Toast('Logged out', 'alert-warning', 2000), class: 'text-error' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Icons (Items Array)
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Dropdown({
|
|
||||||
label: 'Menu',
|
|
||||||
icon: '☰',
|
|
||||||
items: [
|
|
||||||
{ icon: '👤', label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) },
|
|
||||||
{ icon: '⭐', label: 'Favorites', onclick: () => Toast('Favorites', 'alert-info', 2000) },
|
|
||||||
{ icon: '📁', label: 'Documents', onclick: () => Toast('Documents', 'alert-info', 2000) },
|
|
||||||
{ icon: '⚙️', label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Action Dropdown (Items Array)
|
|
||||||
const actionsTarget = document.querySelector('#demo-actions');
|
|
||||||
if (actionsTarget && !actionsTarget.hasChildNodes()) {
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const handleAction = (action) => {
|
|
||||||
Toast(`${action} action`, 'alert-info', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: 'Actions',
|
|
||||||
class: 'dropdown-end',
|
|
||||||
items: [
|
|
||||||
{ icon: '✏️', label: 'Edit', onclick: () => handleAction('Edit') },
|
|
||||||
{ icon: '📋', label: 'Copy', onclick: () => handleAction('Copy') },
|
|
||||||
{ icon: '🗑️', label: 'Delete', onclick: () => handleAction('Delete'), class: 'text-error' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, actionsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. User Dropdown (Items Array)
|
|
||||||
const userTarget = document.querySelector('#demo-user');
|
|
||||||
if (userTarget && !userTarget.hasChildNodes()) {
|
|
||||||
const UserDropdown = () => {
|
|
||||||
return Dropdown({
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-full w-8 h-8 flex items-center justify-center text-sm' }, 'JD')
|
|
||||||
]),
|
|
||||||
'John Doe'
|
|
||||||
]),
|
|
||||||
class: 'dropdown-end',
|
|
||||||
items: [
|
|
||||||
{ label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) },
|
|
||||||
{ label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) },
|
|
||||||
{ label: 'Sign Out', onclick: () => Toast('Signed out', 'alert-warning', 2000), class: 'text-error' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(UserDropdown, userTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Reactive Items
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDropdown = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
const items = () => [
|
|
||||||
{ label: `Count: ${count()}`, onclick: () => {} },
|
|
||||||
{ label: 'Increment', onclick: () => count(count() + 1) },
|
|
||||||
{ label: 'Decrement', onclick: () => count(count() - 1) },
|
|
||||||
{ label: 'Reset', onclick: () => count(0) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: () => `Counter (${count()})`,
|
|
||||||
items: items
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ReactiveDropdown, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Notification Dropdown (Custom Children)
|
|
||||||
const notifTarget = document.querySelector('#demo-notifications');
|
|
||||||
if (notifTarget && !notifTarget.hasChildNodes()) {
|
|
||||||
const NotificationsDropdown = () => {
|
|
||||||
const notifications = $([
|
|
||||||
{ id: 1, title: 'New message', time: '5 min ago', read: false },
|
|
||||||
{ id: 2, title: 'Update available', time: '1 hour ago', read: false },
|
|
||||||
{ id: 3, title: 'Task completed', time: '2 hours ago', read: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const markAsRead = (id) => {
|
|
||||||
notifications(notifications().map(n =>
|
|
||||||
n.id === id ? { ...n, read: true } : n
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
const unreadCount = () => notifications().filter(n => !n.read).length;
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: Span({ class: 'relative' }, [
|
|
||||||
'🔔',
|
|
||||||
() => unreadCount() > 0 ? Span({ class: 'badge badge-xs badge-error absolute -top-1 -right-2' }, unreadCount()) : null
|
|
||||||
]),
|
|
||||||
class: 'dropdown-end',
|
|
||||||
children: () => Div({ class: 'w-80' }, [
|
|
||||||
Div({ class: 'p-3 border-b border-base-300 font-bold' }, `Notifications (${unreadCount()} unread)`),
|
|
||||||
Div({ class: 'max-h-64 overflow-y-auto' }, notifications().map(notif =>
|
|
||||||
Div({
|
|
||||||
class: `p-3 border-b border-base-300 cursor-pointer hover:bg-base-200 ${!notif.read ? 'bg-primary/5' : ''}`,
|
|
||||||
onclick: () => markAsRead(notif.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'font-medium' }, notif.title),
|
|
||||||
Div({ class: 'text-xs opacity-60' }, notif.time),
|
|
||||||
!notif.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New')
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
Div({ class: 'p-2 text-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-ghost w-full',
|
|
||||||
onclick: () => notifications([])
|
|
||||||
}, 'Clear all')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(NotificationsDropdown, notifTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Custom Content Dropdown
|
|
||||||
const customTarget = document.querySelector('#demo-custom');
|
|
||||||
if (customTarget && !customTarget.hasChildNodes()) {
|
|
||||||
const CustomDropdown = () => {
|
|
||||||
const selected = $('Option 1');
|
|
||||||
|
|
||||||
return Dropdown({
|
|
||||||
label: () => selected(),
|
|
||||||
children: () => Div({ class: 'p-4 min-w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Select an option'),
|
|
||||||
Div({ class: 'flex flex-col gap-1' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm justify-start',
|
|
||||||
onclick: () => selected('Option 1')
|
|
||||||
}, 'Option 1'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm justify-start',
|
|
||||||
onclick: () => selected('Option 2')
|
|
||||||
}, 'Option 2'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm justify-start',
|
|
||||||
onclick: () => selected('Option 3')
|
|
||||||
}, 'Option 3')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CustomDropdown, customTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const commonItems = [
|
|
||||||
{ label: 'Item 1', onclick: () => Toast('Item 1', 'alert-info', 2000) },
|
|
||||||
{ label: 'Item 2', onclick: () => Toast('Item 2', 'alert-info', 2000) },
|
|
||||||
{ label: 'Item 3', onclick: () => Toast('Item 3', 'alert-info', 2000) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Dropdown({ label: 'Default', items: commonItems }),
|
|
||||||
Dropdown({ label: 'With Icon', icon: '☰', items: commonItems }),
|
|
||||||
Dropdown({ label: 'End Position', class: 'dropdown-end', items: commonItems })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initDropdownExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initDropdownExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,688 +0,0 @@
|
|||||||
# Fab
|
|
||||||
|
|
||||||
Floating Action Button (FAB) component for primary actions with expandable menu options. Each example uses a container with `position: relative` and explicit height to position the FAB correctly.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Fab`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :--------------- | :----------------------------------------------- |
|
|
||||||
| `icon` | `string \| VNode \| Signal` | `-` | Main FAB icon |
|
|
||||||
| `label` | `string \| VNode \| Signal` | `-` | Text label for main button |
|
|
||||||
| `actions` | `Array<Action> \| Signal<Array>` | `[]` | Array of action buttons that expand from FAB |
|
|
||||||
| `position` | `string` | `'bottom-6 right-6'` | CSS position classes (e.g., 'bottom-6 left-6') |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
### Action Structure
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| :---------- | :--------------------------- | :----------------------------------------------- |
|
|
||||||
| `label` | `string \| VNode` | Label text shown next to action button |
|
|
||||||
| `icon` | `string \| VNode` | Icon for the action button |
|
|
||||||
| `onclick` | `function` | Click handler |
|
|
||||||
| `class` | `string` | Additional CSS classes for the action button |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic FAB
|
|
||||||
|
|
||||||
<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 min-h-[300px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Fab({
|
|
||||||
icon: '➕',
|
|
||||||
actions: [
|
|
||||||
{ icon: '📝', label: 'New Note', onclick: () => Toast('Create note', 'alert-info', 2000) },
|
|
||||||
{ icon: '📷', label: 'Take Photo', onclick: () => Toast('Open camera', 'alert-info', 2000) },
|
|
||||||
{ icon: '📎', label: 'Attach File', onclick: () => Toast('Attach file', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Label
|
|
||||||
|
|
||||||
<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-label" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const LabelDemo = () => {
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Fab({
|
|
||||||
label: 'Create',
|
|
||||||
icon: '✨',
|
|
||||||
actions: [
|
|
||||||
{ icon: '📝', label: 'Document', onclick: () => Toast('New document', 'alert-success', 2000) },
|
|
||||||
{ icon: '🎨', label: 'Design', onclick: () => Toast('New design', 'alert-success', 2000) },
|
|
||||||
{ icon: '📊', label: 'Spreadsheet', onclick: () => Toast('New spreadsheet', 'alert-success', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(LabelDemo, '#demo-label');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Different Positions
|
|
||||||
|
|
||||||
<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-positions" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[500px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const PositionsDemo = () => {
|
|
||||||
const position = $('bottom-6 right-6');
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[500px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute top-4 left-4 z-20 bg-base-200 p-2 rounded-lg shadow' }, [
|
|
||||||
Select({
|
|
||||||
value: position,
|
|
||||||
options: [
|
|
||||||
{ value: 'bottom-6 right-6', label: 'Bottom Right' },
|
|
||||||
{ value: 'bottom-6 left-6', label: 'Bottom Left' },
|
|
||||||
{ value: 'top-6 right-6', label: 'Top Right' },
|
|
||||||
{ value: 'top-6 left-6', label: 'Top Left' }
|
|
||||||
],
|
|
||||||
onchange: (e) => position(e.target.value)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'absolute inset-0 flex items-center justify-center text-sm opacity-50 pointer-events-none' }, [
|
|
||||||
'FAB position changes relative to this container'
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
position: () => position(),
|
|
||||||
icon: '🧭',
|
|
||||||
actions: [
|
|
||||||
{ icon: '⬅️', label: 'Bottom Left', onclick: () => position('bottom-6 left-6') },
|
|
||||||
{ icon: '➡️', label: 'Bottom Right', onclick: () => position('bottom-6 right-6') },
|
|
||||||
{ icon: '⬆️', label: 'Top Right', onclick: () => position('top-6 right-6') },
|
|
||||||
{ icon: '⬇️', label: 'Top Left', onclick: () => position('top-6 left-6') }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PositionsDemo, '#demo-positions');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Color 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-colors" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ColorsDemo = () => {
|
|
||||||
const variant = $('primary');
|
|
||||||
|
|
||||||
const variants = {
|
|
||||||
primary: { class: 'btn-primary', icon: '🔵' },
|
|
||||||
secondary: { class: 'btn-secondary', icon: '🟣' },
|
|
||||||
accent: { class: 'btn-accent', icon: '🔴' },
|
|
||||||
info: { class: 'btn-info', icon: '🔷' },
|
|
||||||
success: { class: 'btn-success', icon: '🟢' },
|
|
||||||
warning: { class: 'btn-warning', icon: '🟡' },
|
|
||||||
error: { class: 'btn-error', icon: '🔴' }
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute top-4 left-4 z-20 bg-base-200 p-2 rounded-lg shadow' }, [
|
|
||||||
Select({
|
|
||||||
value: variant,
|
|
||||||
options: Object.keys(variants).map(v => ({ value: v, label: v.charAt(0).toUpperCase() + v.slice(1) })),
|
|
||||||
onchange: (e) => variant(e.target.value)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
class: variants[variant()].class,
|
|
||||||
icon: variants[variant()].icon,
|
|
||||||
actions: [
|
|
||||||
{ icon: '📝', label: 'Action 1', onclick: () => Toast('Action 1', 'alert-info', 2000) },
|
|
||||||
{ icon: '🎨', label: 'Action 2', onclick: () => Toast('Action 2', 'alert-info', 2000) },
|
|
||||||
{ icon: '⚙️', label: 'Action 3', onclick: () => Toast('Action 3', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ColorsDemo, '#demo-colors');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Actions
|
|
||||||
|
|
||||||
<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 min-h-[300px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ReactiveActions = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
const actions = () => [
|
|
||||||
{
|
|
||||||
icon: '🔢',
|
|
||||||
label: `Count: ${count()}`,
|
|
||||||
onclick: () => {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '➕',
|
|
||||||
label: 'Increment',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '➖',
|
|
||||||
label: 'Decrement',
|
|
||||||
onclick: () => count(count() - 1)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🔄',
|
|
||||||
label: 'Reset',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Fab({
|
|
||||||
icon: () => count() > 0 ? `🔢 ${count()}` : '🎛️',
|
|
||||||
actions: actions
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveActions, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Document Actions
|
|
||||||
|
|
||||||
<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-document" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DocumentActions = () => {
|
|
||||||
const saved = $(false);
|
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
saved(true);
|
|
||||||
Toast('Document saved!', 'alert-success', 2000);
|
|
||||||
setTimeout(() => saved(false), 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute inset-0 flex flex-col items-center justify-center' }, [
|
|
||||||
Div({ class: 'text-6xl mb-4' }, '📄'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Untitled Document'),
|
|
||||||
() => saved() ? Div({ class: 'mt-4' }, Alert({ type: 'success', message: '✓ Saved successfully' })) : null
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
icon: '✏️',
|
|
||||||
actions: [
|
|
||||||
{ icon: '💾', label: 'Save', onclick: handleSave },
|
|
||||||
{ icon: '📋', label: 'Copy', onclick: () => Toast('Copied!', 'alert-info', 2000) },
|
|
||||||
{ icon: '✂️', label: 'Cut', onclick: () => Toast('Cut!', 'alert-info', 2000) },
|
|
||||||
{ icon: '📎', label: 'Share', onclick: () => Toast('Share dialog', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DocumentActions, '#demo-document');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Messaging FAB
|
|
||||||
|
|
||||||
<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-messaging" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const MessagingFAB = () => {
|
|
||||||
const unread = $(3);
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute inset-0 flex flex-col items-center justify-center' }, [
|
|
||||||
Div({ class: 'text-6xl mb-4' }, '💬'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Messages'),
|
|
||||||
() => unread() > 0 ? Div({ class: 'badge badge-error mt-2' }, `${unread()} unread`) : null
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
icon: () => `💬${unread() > 0 ? ` ${unread()}` : ''}`,
|
|
||||||
class: 'btn-primary',
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
icon: '👤',
|
|
||||||
label: 'New Message',
|
|
||||||
onclick: () => Toast('Start new conversation', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '👥',
|
|
||||||
label: 'Group Chat',
|
|
||||||
onclick: () => Toast('Create group', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📞',
|
|
||||||
label: 'Voice Call',
|
|
||||||
onclick: () => Toast('Start call', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📹',
|
|
||||||
label: 'Video Call',
|
|
||||||
onclick: () => Toast('Start video call', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🔔',
|
|
||||||
label: () => `Mark as read (${unread()})`,
|
|
||||||
onclick: () => {
|
|
||||||
unread(0);
|
|
||||||
Toast('All messages read', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MessagingFAB, '#demo-messaging');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Flower Style FAB
|
|
||||||
|
|
||||||
<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-flower" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const FlowerDemo = () => {
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute inset-0 flex items-center justify-center text-sm opacity-50' }, [
|
|
||||||
'Flower style FAB (quarter circle arrangement)'
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
icon: '🌸',
|
|
||||||
class: 'fab-flower',
|
|
||||||
actions: [
|
|
||||||
{ icon: '📷', label: 'Camera', onclick: () => Toast('Camera', 'alert-info', 2000) },
|
|
||||||
{ icon: '🎨', label: 'Gallery', onclick: () => Toast('Gallery', 'alert-info', 2000) },
|
|
||||||
{ icon: '🎤', label: 'Voice', onclick: () => Toast('Voice', 'alert-info', 2000) },
|
|
||||||
{ icon: '📍', label: 'Location', onclick: () => Toast('Location', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FlowerDemo, '#demo-flower');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 min-h-[400px] relative"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const actions = [
|
|
||||||
{ icon: '⭐', label: 'Favorite', onclick: () => Toast('Favorited', 'alert-info', 2000) },
|
|
||||||
{ icon: '🔔', label: 'Remind', onclick: () => Toast('Reminder set', 'alert-info', 2000) },
|
|
||||||
{ icon: '📅', label: 'Schedule', onclick: () => Toast('Scheduled', 'alert-info', 2000) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[400px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4 p-4 h-full' }, [
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Primary'),
|
|
||||||
Fab({ icon: '🔵', class: 'btn-primary', actions, position: 'bottom-6 left-6' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Secondary'),
|
|
||||||
Fab({ icon: '🟣', class: 'btn-secondary', actions, position: 'bottom-6 right-6' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Accent'),
|
|
||||||
Fab({ icon: '🔴', class: 'btn-accent', actions, position: 'top-6 left-6' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Success'),
|
|
||||||
Fab({ icon: '🟢', class: 'btn-success', actions, position: 'top-6 right-6' })
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initFabExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic FAB
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Fab({
|
|
||||||
icon: '➕',
|
|
||||||
actions: [
|
|
||||||
{ icon: '📝', label: 'New Note', onclick: () => Toast('Create note', 'alert-info', 2000) },
|
|
||||||
{ icon: '📷', label: 'Take Photo', onclick: () => Toast('Open camera', 'alert-info', 2000) },
|
|
||||||
{ icon: '📎', label: 'Attach File', onclick: () => Toast('Attach file', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Label
|
|
||||||
const labelTarget = document.querySelector('#demo-label');
|
|
||||||
if (labelTarget && !labelTarget.hasChildNodes()) {
|
|
||||||
const LabelDemo = () => {
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Fab({
|
|
||||||
label: 'Create',
|
|
||||||
icon: '✨',
|
|
||||||
actions: [
|
|
||||||
{ icon: '📝', label: 'Document', onclick: () => Toast('New document', 'alert-success', 2000) },
|
|
||||||
{ icon: '🎨', label: 'Design', onclick: () => Toast('New design', 'alert-success', 2000) },
|
|
||||||
{ icon: '📊', label: 'Spreadsheet', onclick: () => Toast('New spreadsheet', 'alert-success', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(LabelDemo, labelTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Different Positions
|
|
||||||
const positionsTarget = document.querySelector('#demo-positions');
|
|
||||||
if (positionsTarget && !positionsTarget.hasChildNodes()) {
|
|
||||||
const PositionsDemo = () => {
|
|
||||||
const position = $('bottom-6 right-6');
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[500px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute top-4 left-4 z-20 bg-base-200 p-2 rounded-lg shadow' }, [
|
|
||||||
Select({
|
|
||||||
value: position,
|
|
||||||
options: [
|
|
||||||
{ value: 'bottom-6 right-6', label: 'Bottom Right' },
|
|
||||||
{ value: 'bottom-6 left-6', label: 'Bottom Left' },
|
|
||||||
{ value: 'top-6 right-6', label: 'Top Right' },
|
|
||||||
{ value: 'top-6 left-6', label: 'Top Left' }
|
|
||||||
],
|
|
||||||
onchange: (e) => position(e.target.value)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'absolute inset-0 flex items-center justify-center text-sm opacity-50 pointer-events-none' }, [
|
|
||||||
'FAB position changes relative to this container'
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
position: () => position(),
|
|
||||||
icon: '🧭',
|
|
||||||
actions: [
|
|
||||||
{ icon: '⬅️', label: 'Bottom Left', onclick: () => position('bottom-6 left-6') },
|
|
||||||
{ icon: '➡️', label: 'Bottom Right', onclick: () => position('bottom-6 right-6') },
|
|
||||||
{ icon: '⬆️', label: 'Top Right', onclick: () => position('top-6 right-6') },
|
|
||||||
{ icon: '⬇️', label: 'Top Left', onclick: () => position('top-6 left-6') }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PositionsDemo, positionsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Color Variants
|
|
||||||
const colorsTarget = document.querySelector('#demo-colors');
|
|
||||||
if (colorsTarget && !colorsTarget.hasChildNodes()) {
|
|
||||||
const ColorsDemo = () => {
|
|
||||||
const variant = $('primary');
|
|
||||||
|
|
||||||
const variants = {
|
|
||||||
primary: { class: 'btn-primary', icon: '🔵' },
|
|
||||||
secondary: { class: 'btn-secondary', icon: '🟣' },
|
|
||||||
accent: { class: 'btn-accent', icon: '🔴' },
|
|
||||||
info: { class: 'btn-info', icon: '🔷' },
|
|
||||||
success: { class: 'btn-success', icon: '🟢' },
|
|
||||||
warning: { class: 'btn-warning', icon: '🟡' },
|
|
||||||
error: { class: 'btn-error', icon: '🔴' }
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute top-4 left-4 z-20 bg-base-200 p-2 rounded-lg shadow' }, [
|
|
||||||
Select({
|
|
||||||
value: variant,
|
|
||||||
options: Object.keys(variants).map(v => ({ value: v, label: v.charAt(0).toUpperCase() + v.slice(1) })),
|
|
||||||
onchange: (e) => variant(e.target.value)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
class: variants[variant()].class,
|
|
||||||
icon: variants[variant()].icon,
|
|
||||||
actions: [
|
|
||||||
{ icon: '📝', label: 'Action 1', onclick: () => Toast('Action 1', 'alert-info', 2000) },
|
|
||||||
{ icon: '🎨', label: 'Action 2', onclick: () => Toast('Action 2', 'alert-info', 2000) },
|
|
||||||
{ icon: '⚙️', label: 'Action 3', onclick: () => Toast('Action 3', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ColorsDemo, colorsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Reactive Actions
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveActions = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
const actions = () => [
|
|
||||||
{
|
|
||||||
icon: '🔢',
|
|
||||||
label: `Count: ${count()}`,
|
|
||||||
onclick: () => {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '➕',
|
|
||||||
label: 'Increment',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '➖',
|
|
||||||
label: 'Decrement',
|
|
||||||
onclick: () => count(count() - 1)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🔄',
|
|
||||||
label: 'Reset',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Fab({
|
|
||||||
icon: () => count() > 0 ? `🔢 ${count()}` : '🎛️',
|
|
||||||
actions: actions
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveActions, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Document Actions
|
|
||||||
const documentTarget = document.querySelector('#demo-document');
|
|
||||||
if (documentTarget && !documentTarget.hasChildNodes()) {
|
|
||||||
const DocumentActions = () => {
|
|
||||||
const saved = $(false);
|
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
saved(true);
|
|
||||||
Toast('Document saved!', 'alert-success', 2000);
|
|
||||||
setTimeout(() => saved(false), 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute inset-0 flex flex-col items-center justify-center' }, [
|
|
||||||
Div({ class: 'text-6xl mb-4' }, '📄'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Untitled Document'),
|
|
||||||
() => saved() ? Div({ class: 'mt-4' }, Alert({ type: 'success', message: '✓ Saved successfully' })) : null
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
icon: '✏️',
|
|
||||||
actions: [
|
|
||||||
{ icon: '💾', label: 'Save', onclick: handleSave },
|
|
||||||
{ icon: '📋', label: 'Copy', onclick: () => Toast('Copied!', 'alert-info', 2000) },
|
|
||||||
{ icon: '✂️', label: 'Cut', onclick: () => Toast('Cut!', 'alert-info', 2000) },
|
|
||||||
{ icon: '📎', label: 'Share', onclick: () => Toast('Share dialog', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DocumentActions, documentTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Messaging FAB
|
|
||||||
const messagingTarget = document.querySelector('#demo-messaging');
|
|
||||||
if (messagingTarget && !messagingTarget.hasChildNodes()) {
|
|
||||||
const MessagingFAB = () => {
|
|
||||||
const unread = $(3);
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute inset-0 flex flex-col items-center justify-center' }, [
|
|
||||||
Div({ class: 'text-6xl mb-4' }, '💬'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Messages'),
|
|
||||||
() => unread() > 0 ? Div({ class: 'badge badge-error mt-2' }, `${unread()} unread`) : null
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
icon: () => `💬${unread() > 0 ? ` ${unread()}` : ''}`,
|
|
||||||
class: 'btn-primary',
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
icon: '👤',
|
|
||||||
label: 'New Message',
|
|
||||||
onclick: () => Toast('Start new conversation', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '👥',
|
|
||||||
label: 'Group Chat',
|
|
||||||
onclick: () => Toast('Create group', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📞',
|
|
||||||
label: 'Voice Call',
|
|
||||||
onclick: () => Toast('Start call', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📹',
|
|
||||||
label: 'Video Call',
|
|
||||||
onclick: () => Toast('Start video call', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🔔',
|
|
||||||
label: () => `Mark as read (${unread()})`,
|
|
||||||
onclick: () => {
|
|
||||||
unread(0);
|
|
||||||
Toast('All messages read', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MessagingFAB, messagingTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Flower Style FAB
|
|
||||||
const flowerTarget = document.querySelector('#demo-flower');
|
|
||||||
if (flowerTarget && !flowerTarget.hasChildNodes()) {
|
|
||||||
const FlowerDemo = () => {
|
|
||||||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'absolute inset-0 flex items-center justify-center text-sm opacity-50' }, [
|
|
||||||
'Flower style FAB (quarter circle arrangement)'
|
|
||||||
]),
|
|
||||||
Fab({
|
|
||||||
icon: '🌸',
|
|
||||||
class: 'fab-flower',
|
|
||||||
actions: [
|
|
||||||
{ icon: '📷', label: 'Camera', onclick: () => Toast('Camera', 'alert-info', 2000) },
|
|
||||||
{ icon: '🎨', label: 'Gallery', onclick: () => Toast('Gallery', 'alert-info', 2000) },
|
|
||||||
{ icon: '🎤', label: 'Voice', onclick: () => Toast('Voice', 'alert-info', 2000) },
|
|
||||||
{ icon: '📍', label: 'Location', onclick: () => Toast('Location', 'alert-info', 2000) }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FlowerDemo, flowerTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const actions = [
|
|
||||||
{ icon: '⭐', label: 'Favorite', onclick: () => Toast('Favorited', 'alert-info', 2000) },
|
|
||||||
{ icon: '🔔', label: 'Remind', onclick: () => Toast('Reminder set', 'alert-info', 2000) },
|
|
||||||
{ icon: '📅', label: 'Schedule', onclick: () => Toast('Scheduled', 'alert-info', 2000) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'relative h-[400px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4 p-4 h-full' }, [
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Primary'),
|
|
||||||
Fab({ icon: '🔵', class: 'btn-primary', actions, position: 'bottom-6 left-6' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Secondary'),
|
|
||||||
Fab({ icon: '🟣', class: 'btn-secondary', actions, position: 'bottom-6 right-6' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Accent'),
|
|
||||||
Fab({ icon: '🔴', class: 'btn-accent', actions, position: 'top-6 left-6' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
|
||||||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Success'),
|
|
||||||
Fab({ icon: '🟢', class: 'btn-success', actions, position: 'top-6 right-6' })
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initFabExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initFabExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,549 +0,0 @@
|
|||||||
# Fieldset
|
|
||||||
|
|
||||||
Fieldset component for grouping form fields with optional legend and consistent styling.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Fieldset`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `legend` | `string \| VNode \| Signal` | `-` | Fieldset legend/title |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `VNode \| Array<VNode>` | Required | Form fields or content inside the fieldset |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Fieldset
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Fieldset({
|
|
||||||
legend: 'User Information',
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Full Name', placeholder: 'Enter your name' }),
|
|
||||||
Input({ label: 'Email', type: 'email', placeholder: 'user@example.com' }),
|
|
||||||
Input({ label: 'Phone', type: 'tel', placeholder: '+1 234 567 890' })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Reactive Legend
|
|
||||||
|
|
||||||
<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 name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const isValid = () => name().length > 0 && email().includes('@');
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: () => isValid() ? '✓ Valid Form' : '✗ Incomplete Form',
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Full Name',
|
|
||||||
value: name,
|
|
||||||
oninput: (e) => name(e.target.value),
|
|
||||||
placeholder: 'Enter your name'
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
oninput: (e) => email(e.target.value),
|
|
||||||
placeholder: 'user@example.com'
|
|
||||||
}),
|
|
||||||
() => isValid()
|
|
||||||
? Alert({ type: 'success', message: 'Form is ready to submit' })
|
|
||||||
: Alert({ type: 'warning', message: 'Please fill all required fields' })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Address Form
|
|
||||||
|
|
||||||
<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-address" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AddressDemo = () => {
|
|
||||||
const address = $('');
|
|
||||||
const city = $('');
|
|
||||||
const zip = $('');
|
|
||||||
const country = $('us');
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['📍', 'Shipping Address']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Street Address', value: address, placeholder: '123 Main St', oninput: (e) => address(e.target.value) }),
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4' }, [
|
|
||||||
Input({ label: 'City', value: city, placeholder: 'City', oninput: (e) => city(e.target.value) }),
|
|
||||||
Input({ label: 'ZIP Code', value: zip, placeholder: 'ZIP', oninput: (e) => zip(e.target.value) })
|
|
||||||
]),
|
|
||||||
Select({
|
|
||||||
label: 'Country',
|
|
||||||
value: country,
|
|
||||||
options: [
|
|
||||||
{ value: 'us', label: 'United States' },
|
|
||||||
{ value: 'ca', label: 'Canada' },
|
|
||||||
{ value: 'mx', label: 'Mexico' }
|
|
||||||
],
|
|
||||||
onchange: (e) => country(e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AddressDemo, '#demo-address');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Payment Method
|
|
||||||
|
|
||||||
<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');
|
|
||||||
const cardNumber = $('');
|
|
||||||
const expiry = $('');
|
|
||||||
const cvv = $('');
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['💳', 'Payment Details']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Div({ class: 'flex gap-4' }, [
|
|
||||||
Radio({ label: 'Credit Card', value: method, radioValue: 'credit', onclick: () => method('credit') }),
|
|
||||||
Radio({ label: 'PayPal', value: method, radioValue: 'paypal', onclick: () => method('paypal') }),
|
|
||||||
Radio({ label: 'Bank Transfer', value: method, radioValue: 'bank', onclick: () => method('bank') })
|
|
||||||
]),
|
|
||||||
() => method() === 'credit' ? Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Card Number', value: cardNumber, placeholder: '1234 5678 9012 3456', oninput: (e) => cardNumber(e.target.value) }),
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4' }, [
|
|
||||||
Input({ label: 'Expiry Date', value: expiry, placeholder: 'MM/YY', oninput: (e) => expiry(e.target.value) }),
|
|
||||||
Input({ label: 'CVV', type: 'password', value: cvv, placeholder: '123', oninput: (e) => cvv(e.target.value) })
|
|
||||||
])
|
|
||||||
]) : null,
|
|
||||||
() => method() === 'paypal' ? Alert({ type: 'info', message: 'You will be redirected to PayPal after confirming.' }) : null,
|
|
||||||
() => method() === 'bank' ? Alert({ type: 'warning', message: 'Bank transfer details will be sent via email.' }) : null
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PaymentDemo, '#demo-payment');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Preferences Panel
|
|
||||||
|
|
||||||
<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-preferences" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const PreferencesDemo = () => {
|
|
||||||
const theme = $('light');
|
|
||||||
const language = $('en');
|
|
||||||
const notifications = $(true);
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['⚙️', 'Preferences']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Div({ class: 'form-control' }, [
|
|
||||||
Span({ class: 'label-text mb-2' }, 'Theme'),
|
|
||||||
Div({ class: 'flex gap-4' }, [
|
|
||||||
Radio({ label: 'Light', value: theme, radioValue: 'light', onclick: () => theme('light') }),
|
|
||||||
Radio({ label: 'Dark', value: theme, radioValue: 'dark', onclick: () => theme('dark') }),
|
|
||||||
Radio({ label: 'System', value: theme, radioValue: 'system', onclick: () => theme('system') })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Select({
|
|
||||||
label: 'Language',
|
|
||||||
value: language,
|
|
||||||
options: [
|
|
||||||
{ value: 'en', label: 'English' },
|
|
||||||
{ value: 'es', label: 'Español' },
|
|
||||||
{ value: 'fr', label: 'Français' }
|
|
||||||
],
|
|
||||||
onchange: (e) => language(e.target.value)
|
|
||||||
}),
|
|
||||||
Checkbox({
|
|
||||||
label: 'Enable notifications',
|
|
||||||
value: notifications,
|
|
||||||
onclick: () => notifications(!notifications())
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PreferencesDemo, '#demo-preferences');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Registration Form
|
|
||||||
|
|
||||||
<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-registration" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const RegistrationDemo = () => {
|
|
||||||
const username = $('');
|
|
||||||
const email = $('');
|
|
||||||
const password = $('');
|
|
||||||
const confirmPassword = $('');
|
|
||||||
const accepted = $(false);
|
|
||||||
|
|
||||||
const passwordsMatch = () => password() === confirmPassword();
|
|
||||||
const isFormValid = () => username() && email().includes('@') && password().length >= 6 && passwordsMatch() && accepted();
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (isFormValid()) {
|
|
||||||
Toast('Registration successful!', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['📝', 'Create Account']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Username', value: username, placeholder: 'Choose a username', oninput: (e) => username(e.target.value) }),
|
|
||||||
Input({ label: 'Email', type: 'email', value: email, placeholder: 'your@email.com', oninput: (e) => email(e.target.value) }),
|
|
||||||
Input({ label: 'Password', type: 'password', value: password, placeholder: 'Min. 6 characters', oninput: (e) => password(e.target.value) }),
|
|
||||||
Input({
|
|
||||||
label: 'Confirm Password',
|
|
||||||
type: 'password',
|
|
||||||
value: confirmPassword,
|
|
||||||
error: () => confirmPassword() && !passwordsMatch() ? 'Passwords do not match' : '',
|
|
||||||
oninput: (e) => confirmPassword(e.target.value)
|
|
||||||
}),
|
|
||||||
Checkbox({
|
|
||||||
label: 'I accept the Terms and Conditions',
|
|
||||||
value: accepted,
|
|
||||||
onclick: () => accepted(!accepted())
|
|
||||||
}),
|
|
||||||
() => !isFormValid() && (username() || email() || password()) ? Alert({ type: 'warning', message: 'Please complete all fields correctly' }) : null,
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary w-full',
|
|
||||||
onclick: handleSubmit,
|
|
||||||
disabled: () => !isFormValid()
|
|
||||||
}, 'Register')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(RegistrationDemo, '#demo-registration');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 commonContent = Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Field 1', placeholder: 'Enter value' }),
|
|
||||||
Input({ label: 'Field 2', placeholder: 'Enter value' }),
|
|
||||||
Button({ class: 'btn btn-primary' }, 'Submit')
|
|
||||||
]);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Fieldset({ legend: 'Default Fieldset', class: 'w-full' }, [commonContent]),
|
|
||||||
Fieldset({ legend: 'With Shadow', class: 'w-full shadow-lg' }, [commonContent]),
|
|
||||||
Fieldset({ legend: 'With Background', class: 'w-full bg-base-100' }, [commonContent])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initFieldsetExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Fieldset
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Fieldset({
|
|
||||||
legend: 'User Information',
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Full Name', placeholder: 'Enter your name' }),
|
|
||||||
Input({ label: 'Email', type: 'email', placeholder: 'user@example.com' }),
|
|
||||||
Input({ label: 'Phone', type: 'tel', placeholder: '+1 234 567 890' })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Reactive Legend
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const isValid = () => name().length > 0 && email().includes('@');
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: () => isValid() ? '✓ Valid Form' : '✗ Incomplete Form',
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Full Name',
|
|
||||||
value: name,
|
|
||||||
oninput: (e) => name(e.target.value),
|
|
||||||
placeholder: 'Enter your name'
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
oninput: (e) => email(e.target.value),
|
|
||||||
placeholder: 'user@example.com'
|
|
||||||
}),
|
|
||||||
() => isValid()
|
|
||||||
? Alert({ type: 'success', message: 'Form is ready to submit' })
|
|
||||||
: Alert({ type: 'warning', message: 'Please fill all required fields' })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Address Form
|
|
||||||
const addressTarget = document.querySelector('#demo-address');
|
|
||||||
if (addressTarget && !addressTarget.hasChildNodes()) {
|
|
||||||
const AddressDemo = () => {
|
|
||||||
const address = $('');
|
|
||||||
const city = $('');
|
|
||||||
const zip = $('');
|
|
||||||
const country = $('us');
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['📍', 'Shipping Address']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Street Address', value: address, placeholder: '123 Main St', oninput: (e) => address(e.target.value) }),
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4' }, [
|
|
||||||
Input({ label: 'City', value: city, placeholder: 'City', oninput: (e) => city(e.target.value) }),
|
|
||||||
Input({ label: 'ZIP Code', value: zip, placeholder: 'ZIP', oninput: (e) => zip(e.target.value) })
|
|
||||||
]),
|
|
||||||
Select({
|
|
||||||
label: 'Country',
|
|
||||||
value: country,
|
|
||||||
options: [
|
|
||||||
{ value: 'us', label: 'United States' },
|
|
||||||
{ value: 'ca', label: 'Canada' },
|
|
||||||
{ value: 'mx', label: 'Mexico' }
|
|
||||||
],
|
|
||||||
onchange: (e) => country(e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AddressDemo, addressTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Payment Method
|
|
||||||
const paymentTarget = document.querySelector('#demo-payment');
|
|
||||||
if (paymentTarget && !paymentTarget.hasChildNodes()) {
|
|
||||||
const PaymentDemo = () => {
|
|
||||||
const method = $('credit');
|
|
||||||
const cardNumber = $('');
|
|
||||||
const expiry = $('');
|
|
||||||
const cvv = $('');
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['💳', 'Payment Details']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Div({ class: 'flex gap-4' }, [
|
|
||||||
Radio({ label: 'Credit Card', value: method, radioValue: 'credit', onclick: () => method('credit') }),
|
|
||||||
Radio({ label: 'PayPal', value: method, radioValue: 'paypal', onclick: () => method('paypal') }),
|
|
||||||
Radio({ label: 'Bank Transfer', value: method, radioValue: 'bank', onclick: () => method('bank') })
|
|
||||||
]),
|
|
||||||
() => method() === 'credit' ? Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Card Number', value: cardNumber, placeholder: '1234 5678 9012 3456', oninput: (e) => cardNumber(e.target.value) }),
|
|
||||||
Div({ class: 'grid grid-cols-2 gap-4' }, [
|
|
||||||
Input({ label: 'Expiry Date', value: expiry, placeholder: 'MM/YY', oninput: (e) => expiry(e.target.value) }),
|
|
||||||
Input({ label: 'CVV', type: 'password', value: cvv, placeholder: '123', oninput: (e) => cvv(e.target.value) })
|
|
||||||
])
|
|
||||||
]) : null,
|
|
||||||
() => method() === 'paypal' ? Alert({ type: 'info', message: 'You will be redirected to PayPal after confirming.' }) : null,
|
|
||||||
() => method() === 'bank' ? Alert({ type: 'warning', message: 'Bank transfer details will be sent via email.' }) : null
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PaymentDemo, paymentTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Preferences Panel
|
|
||||||
const preferencesTarget = document.querySelector('#demo-preferences');
|
|
||||||
if (preferencesTarget && !preferencesTarget.hasChildNodes()) {
|
|
||||||
const PreferencesDemo = () => {
|
|
||||||
const theme = $('light');
|
|
||||||
const language = $('en');
|
|
||||||
const notifications = $(true);
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['⚙️', 'Preferences']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Div({ class: 'form-control' }, [
|
|
||||||
Span({ class: 'label-text mb-2' }, 'Theme'),
|
|
||||||
Div({ class: 'flex gap-4' }, [
|
|
||||||
Radio({ label: 'Light', value: theme, radioValue: 'light', onclick: () => theme('light') }),
|
|
||||||
Radio({ label: 'Dark', value: theme, radioValue: 'dark', onclick: () => theme('dark') }),
|
|
||||||
Radio({ label: 'System', value: theme, radioValue: 'system', onclick: () => theme('system') })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Select({
|
|
||||||
label: 'Language',
|
|
||||||
value: language,
|
|
||||||
options: [
|
|
||||||
{ value: 'en', label: 'English' },
|
|
||||||
{ value: 'es', label: 'Español' },
|
|
||||||
{ value: 'fr', label: 'Français' }
|
|
||||||
],
|
|
||||||
onchange: (e) => language(e.target.value)
|
|
||||||
}),
|
|
||||||
Checkbox({
|
|
||||||
label: 'Enable notifications',
|
|
||||||
value: notifications,
|
|
||||||
onclick: () => notifications(!notifications())
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PreferencesDemo, preferencesTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Registration Form
|
|
||||||
const registrationTarget = document.querySelector('#demo-registration');
|
|
||||||
if (registrationTarget && !registrationTarget.hasChildNodes()) {
|
|
||||||
const RegistrationDemo = () => {
|
|
||||||
const username = $('');
|
|
||||||
const email = $('');
|
|
||||||
const password = $('');
|
|
||||||
const confirmPassword = $('');
|
|
||||||
const accepted = $(false);
|
|
||||||
|
|
||||||
const passwordsMatch = () => password() === confirmPassword();
|
|
||||||
const isFormValid = () => username() && email().includes('@') && password().length >= 6 && passwordsMatch() && accepted();
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (isFormValid()) {
|
|
||||||
Toast('Registration successful!', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Fieldset({
|
|
||||||
legend: Span({ class: 'flex items-center gap-2' }, ['📝', 'Create Account']),
|
|
||||||
class: 'w-full max-w-md mx-auto'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Username', value: username, placeholder: 'Choose a username', oninput: (e) => username(e.target.value) }),
|
|
||||||
Input({ label: 'Email', type: 'email', value: email, placeholder: 'your@email.com', oninput: (e) => email(e.target.value) }),
|
|
||||||
Input({ label: 'Password', type: 'password', value: password, placeholder: 'Min. 6 characters', oninput: (e) => password(e.target.value) }),
|
|
||||||
Input({
|
|
||||||
label: 'Confirm Password',
|
|
||||||
type: 'password',
|
|
||||||
value: confirmPassword,
|
|
||||||
error: () => confirmPassword() && !passwordsMatch() ? 'Passwords do not match' : '',
|
|
||||||
oninput: (e) => confirmPassword(e.target.value)
|
|
||||||
}),
|
|
||||||
Checkbox({
|
|
||||||
label: 'I accept the Terms and Conditions',
|
|
||||||
value: accepted,
|
|
||||||
onclick: () => accepted(!accepted())
|
|
||||||
}),
|
|
||||||
() => !isFormValid() && (username() || email() || password()) ? Alert({ type: 'warning', message: 'Please complete all fields correctly' }) : null,
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary w-full',
|
|
||||||
onclick: handleSubmit,
|
|
||||||
disabled: () => !isFormValid()
|
|
||||||
}, 'Register')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(RegistrationDemo, registrationTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const commonContent = Div({ class: 'space-y-4' }, [
|
|
||||||
Input({ label: 'Field 1', placeholder: 'Enter value' }),
|
|
||||||
Input({ label: 'Field 2', placeholder: 'Enter value' }),
|
|
||||||
Button({ class: 'btn btn-primary' }, 'Submit')
|
|
||||||
]);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Fieldset({ legend: 'Default Fieldset', class: 'w-full' }, [commonContent]),
|
|
||||||
Fieldset({ legend: 'With Shadow', class: 'w-full shadow-lg' }, [commonContent]),
|
|
||||||
Fieldset({ legend: 'With Background', class: 'w-full bg-base-100' }, [commonContent])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initFieldsetExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initFieldsetExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,556 +0,0 @@
|
|||||||
# Indicator
|
|
||||||
|
|
||||||
Indicator component for adding badges, status markers, or notifications to elements. Perfect for showing counts, online status, or alerts.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Indicator`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `badge` | `string \| VNode \| Signal` | `-` | Content to display as indicator |
|
|
||||||
| `badgeClass` | `string` | `''` | Additional CSS classes for the badge |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes for the container |
|
|
||||||
| `children` | `VNode` | `-` | Element to attach the indicator to |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Indicator
|
|
||||||
|
|
||||||
<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-wrap gap-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Indicator({ badge: '3', badgeClass: 'badge-primary' }, [
|
|
||||||
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '📦')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '99+', badgeClass: 'badge-secondary' }, [
|
|
||||||
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: 'New', badgeClass: 'badge-accent' }, [
|
|
||||||
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '✨')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Online Status Indicator
|
|
||||||
|
|
||||||
<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-status" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const StatusDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JD')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-warning badge-xs' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JS')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-error badge-xs' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'BC')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(StatusDemo, '#demo-status');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Counter
|
|
||||||
|
|
||||||
<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 items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Indicator({
|
|
||||||
badge: () => count() > 0 ? count() : null,
|
|
||||||
badgeClass: 'badge-primary'
|
|
||||||
}, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-lg btn-primary',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
}, 'Notifications')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => count(Math.max(0, count() - 1))
|
|
||||||
}, 'Decrease'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-ghost',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}, 'Clear')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Shopping Cart
|
|
||||||
|
|
||||||
<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-cart" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CartDemo = () => {
|
|
||||||
const cart = $([
|
|
||||||
{ id: 1, name: 'Product 1', price: 29 },
|
|
||||||
{ id: 2, name: 'Product 2', price: 49 }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const addItem = () => {
|
|
||||||
const newId = Math.max(...cart().map(i => i.id), 0) + 1;
|
|
||||||
cart([...cart(), { id: newId, name: `Product ${newId}`, price: Math.floor(Math.random() * 100) + 10 }]);
|
|
||||||
Toast('Item added to cart', 'alert-success', 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeItem = (id) => {
|
|
||||||
cart(cart().filter(item => item.id !== id));
|
|
||||||
Toast('Item removed', 'alert-info', 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
const total = () => cart().reduce((sum, item) => sum + item.price, 0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Indicator({
|
|
||||||
badge: () => cart().length,
|
|
||||||
badgeClass: 'badge-primary'
|
|
||||||
}, [
|
|
||||||
Button({ class: 'btn', onclick: addItem }, '🛒 Add to Cart')
|
|
||||||
]),
|
|
||||||
Span({ class: 'text-lg font-bold' }, () => `Total: $${total()}`)
|
|
||||||
]),
|
|
||||||
cart().length === 0
|
|
||||||
? Div({ class: 'alert alert-soft text-center' }, 'Cart is empty')
|
|
||||||
: Div({ class: 'flex flex-col gap-2' }, cart().map(item =>
|
|
||||||
Div({ class: 'flex justify-between items-center p-2 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({}, item.name),
|
|
||||||
Div({ class: 'flex gap-2 items-center' }, [
|
|
||||||
Span({ class: 'text-sm font-bold' }, `$${item.price}`),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-ghost btn-circle',
|
|
||||||
onclick: () => removeItem(item.id)
|
|
||||||
}, '✕')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CartDemo, '#demo-cart');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Email Inbox
|
|
||||||
|
|
||||||
<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-inbox" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const InboxDemo = () => {
|
|
||||||
const unread = $(3);
|
|
||||||
const messages = $([
|
|
||||||
{ id: 1, from: 'john@example.com', subject: 'Meeting tomorrow', read: false },
|
|
||||||
{ id: 2, from: 'jane@example.com', subject: 'Project update', read: false },
|
|
||||||
{ id: 3, from: 'bob@example.com', subject: 'Question about design', read: false },
|
|
||||||
{ id: 4, from: 'alice@example.com', subject: 'Weekly report', read: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const markAsRead = (id) => {
|
|
||||||
const msg = messages().find(m => m.id === id);
|
|
||||||
if (!msg.read) {
|
|
||||||
msg.read = true;
|
|
||||||
messages([...messages()]);
|
|
||||||
unread(unread() - 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Indicator({
|
|
||||||
badge: () => unread(),
|
|
||||||
badgeClass: 'badge-primary'
|
|
||||||
}, [
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'Inbox')
|
|
||||||
]),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-ghost',
|
|
||||||
onclick: () => {
|
|
||||||
messages().forEach(m => m.read = true);
|
|
||||||
messages([...messages()]);
|
|
||||||
unread(0);
|
|
||||||
}
|
|
||||||
}, 'Mark all read')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, messages().map(msg =>
|
|
||||||
Div({
|
|
||||||
class: `p-3 rounded-lg cursor-pointer transition-all ${msg.read ? 'bg-base-200 opacity-60' : 'bg-primary/10 border-l-4 border-primary'}`,
|
|
||||||
onclick: () => markAsRead(msg.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'font-medium' }, msg.from),
|
|
||||||
Div({ class: 'text-sm' }, msg.subject),
|
|
||||||
!msg.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New')
|
|
||||||
])
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InboxDemo, '#demo-inbox');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Position
|
|
||||||
|
|
||||||
<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-positions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const PositionsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Top-Left'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Top-Right'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Bottom-Left'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Bottom-Right'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PositionsDemo, '#demo-positions');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Indicator({ badge: '3', badgeClass: 'badge-primary badge-sm' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '📧')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '99+', badgeClass: 'badge-secondary badge-md' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'avatar' }, [
|
|
||||||
Div({ class: 'w-10 h-10 rounded-full bg-primary' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '!', badgeClass: 'badge-error badge-sm' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '⚠️')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initIndicatorExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Indicator
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Indicator({ badge: '3', badgeClass: 'badge-primary' }, [
|
|
||||||
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '📦')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '99+', badgeClass: 'badge-secondary' }, [
|
|
||||||
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: 'New', badgeClass: 'badge-accent' }, [
|
|
||||||
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '✨')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Online Status Indicator
|
|
||||||
const statusTarget = document.querySelector('#demo-status');
|
|
||||||
if (statusTarget && !statusTarget.hasChildNodes()) {
|
|
||||||
const StatusDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JD')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-warning badge-xs' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JS')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-error badge-xs' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'BC')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(StatusDemo, statusTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Reactive Counter
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Indicator({
|
|
||||||
badge: () => count() > 0 ? count() : null,
|
|
||||||
badgeClass: 'badge-primary'
|
|
||||||
}, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-lg btn-primary',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
}, 'Notifications')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => count(Math.max(0, count() - 1))
|
|
||||||
}, 'Decrease'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-ghost',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}, 'Clear')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Shopping Cart
|
|
||||||
const cartTarget = document.querySelector('#demo-cart');
|
|
||||||
if (cartTarget && !cartTarget.hasChildNodes()) {
|
|
||||||
const CartDemo = () => {
|
|
||||||
const cart = $([
|
|
||||||
{ id: 1, name: 'Product 1', price: 29 },
|
|
||||||
{ id: 2, name: 'Product 2', price: 49 }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const addItem = () => {
|
|
||||||
const newId = Math.max(...cart().map(i => i.id), 0) + 1;
|
|
||||||
cart([...cart(), { id: newId, name: `Product ${newId}`, price: Math.floor(Math.random() * 100) + 10 }]);
|
|
||||||
Toast('Item added to cart', 'alert-success', 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeItem = (id) => {
|
|
||||||
cart(cart().filter(item => item.id !== id));
|
|
||||||
Toast('Item removed', 'alert-info', 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
const total = () => cart().reduce((sum, item) => sum + item.price, 0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Indicator({
|
|
||||||
badge: () => cart().length,
|
|
||||||
badgeClass: 'badge-primary'
|
|
||||||
}, [
|
|
||||||
Button({ class: 'btn', onclick: addItem }, '🛒 Add to Cart')
|
|
||||||
]),
|
|
||||||
Span({ class: 'text-lg font-bold' }, () => `Total: $${total()}`)
|
|
||||||
]),
|
|
||||||
cart().length === 0
|
|
||||||
? Div({ class: 'alert alert-soft text-center' }, 'Cart is empty')
|
|
||||||
: Div({ class: 'flex flex-col gap-2' }, cart().map(item =>
|
|
||||||
Div({ class: 'flex justify-between items-center p-2 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({}, item.name),
|
|
||||||
Div({ class: 'flex gap-2 items-center' }, [
|
|
||||||
Span({ class: 'text-sm font-bold' }, `$${item.price}`),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-ghost btn-circle',
|
|
||||||
onclick: () => removeItem(item.id)
|
|
||||||
}, '✕')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CartDemo, cartTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Email Inbox
|
|
||||||
const inboxTarget = document.querySelector('#demo-inbox');
|
|
||||||
if (inboxTarget && !inboxTarget.hasChildNodes()) {
|
|
||||||
const InboxDemo = () => {
|
|
||||||
const unread = $(3);
|
|
||||||
const messages = $([
|
|
||||||
{ id: 1, from: 'john@example.com', subject: 'Meeting tomorrow', read: false },
|
|
||||||
{ id: 2, from: 'jane@example.com', subject: 'Project update', read: false },
|
|
||||||
{ id: 3, from: 'bob@example.com', subject: 'Question about design', read: false },
|
|
||||||
{ id: 4, from: 'alice@example.com', subject: 'Weekly report', read: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const markAsRead = (id) => {
|
|
||||||
const msg = messages().find(m => m.id === id);
|
|
||||||
if (!msg.read) {
|
|
||||||
msg.read = true;
|
|
||||||
messages([...messages()]);
|
|
||||||
unread(unread() - 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Indicator({
|
|
||||||
badge: () => unread(),
|
|
||||||
badgeClass: 'badge-primary'
|
|
||||||
}, [
|
|
||||||
Span({ class: 'text-lg font-bold' }, 'Inbox')
|
|
||||||
]),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-ghost',
|
|
||||||
onclick: () => {
|
|
||||||
messages().forEach(m => m.read = true);
|
|
||||||
messages([...messages()]);
|
|
||||||
unread(0);
|
|
||||||
}
|
|
||||||
}, 'Mark all read')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, messages().map(msg =>
|
|
||||||
Div({
|
|
||||||
class: `p-3 rounded-lg cursor-pointer transition-all ${msg.read ? 'bg-base-200 opacity-60' : 'bg-primary/10 border-l-4 border-primary'}`,
|
|
||||||
onclick: () => markAsRead(msg.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'font-medium' }, msg.from),
|
|
||||||
Div({ class: 'text-sm' }, msg.subject),
|
|
||||||
!msg.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New')
|
|
||||||
])
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InboxDemo, inboxTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Custom Position
|
|
||||||
const positionsTarget = document.querySelector('#demo-positions');
|
|
||||||
if (positionsTarget && !positionsTarget.hasChildNodes()) {
|
|
||||||
const PositionsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Top-Left'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Top-Right'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Bottom-Left'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Bottom-Right'),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PositionsDemo, positionsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Indicator({ badge: '3', badgeClass: 'badge-primary badge-sm' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '📧')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '99+', badgeClass: 'badge-secondary badge-md' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
|
|
||||||
Div({ class: 'avatar' }, [
|
|
||||||
Div({ class: 'w-10 h-10 rounded-full bg-primary' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Indicator({ badge: '!', badgeClass: 'badge-error badge-sm' }, [
|
|
||||||
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '⚠️')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initIndicatorExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initIndicatorExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
# 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>
|
|
||||||
@@ -1,601 +0,0 @@
|
|||||||
|
|
||||||
# List
|
|
||||||
|
|
||||||
List component with custom item rendering, headers, and reactive data binding.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`List`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :--------------- | :----------------------------------------------- |
|
|
||||||
| `items` | `Array \| Signal<Array>` | `[]` | Data array to display |
|
|
||||||
| `header` | `string \| VNode \| Signal` | `-` | Optional header content |
|
|
||||||
| `render` | `function(item, index)` | `-` | Custom render function for each item |
|
|
||||||
| `keyFn` | `function(item, index)` | `(item, idx) => idx` | Unique key function for items |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic List
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const items = ['Apple', 'Banana', 'Orange', 'Grape', 'Mango'];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-3 hover:bg-base-200 transition-colors' }, [
|
|
||||||
Span({ class: 'font-medium' }, item)
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Header
|
|
||||||
|
|
||||||
<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-header" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const HeaderDemo = () => {
|
|
||||||
const users = [
|
|
||||||
{ name: 'John Doe', email: 'john@example.com', status: 'active' },
|
|
||||||
{ name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
|
|
||||||
{ name: 'Bob Johnson', email: 'bob@example.com', status: 'active' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: users,
|
|
||||||
header: Div({ class: 'p-3 bg-primary/10 font-bold border-b border-base-300' }, 'Active Users'),
|
|
||||||
render: (user) => Div({ class: 'p-3 border-b border-base-300 hover:bg-base-200' }, [
|
|
||||||
Div({ class: 'font-medium' }, user.name),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, user.email),
|
|
||||||
Span({ class: `badge badge-sm ${user.status === 'active' ? 'badge-success' : 'badge-ghost'} mt-1` }, user.status)
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(HeaderDemo, '#demo-header');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Icons
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const settings = [
|
|
||||||
{ icon: '🔊', label: 'Sound', description: 'Adjust volume and notifications' },
|
|
||||||
{ icon: '🌙', label: 'Display', description: 'Brightness and dark mode' },
|
|
||||||
{ icon: '🔒', label: 'Privacy', description: 'Security settings' },
|
|
||||||
{ icon: '🌐', label: 'Network', description: 'WiFi and connections' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: settings,
|
|
||||||
render: (item) => Div({ class: 'flex gap-3 p-3 hover:bg-base-200 transition-colors cursor-pointer' }, [
|
|
||||||
Div({ class: 'text-2xl' }, item.icon),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.label),
|
|
||||||
Div({ class: 'text-sm opacity-60' }, item.description)
|
|
||||||
]),
|
|
||||||
Span({ class: 'opacity-40' }, '→')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Badges
|
|
||||||
|
|
||||||
<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-badges" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BadgesDemo = () => {
|
|
||||||
const notifications = [
|
|
||||||
{ id: 1, message: 'New comment on your post', time: '5 min ago', unread: true },
|
|
||||||
{ id: 2, message: 'Your order has been shipped', time: '1 hour ago', unread: true },
|
|
||||||
{ id: 3, message: 'Welcome to the platform!', time: '2 days ago', unread: false },
|
|
||||||
{ id: 4, message: 'Weekly digest available', time: '3 days ago', unread: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: notifications,
|
|
||||||
render: (item) => Div({ class: `flex justify-between items-center p-3 border-b border-base-300 hover:bg-base-200 ${item.unread ? 'bg-primary/5' : ''}` }, [
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.message),
|
|
||||||
Div({ class: 'text-xs opacity-50' }, item.time)
|
|
||||||
]),
|
|
||||||
item.unread ? Span({ class: 'badge badge-primary badge-sm' }, 'New') : null
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BadgesDemo, '#demo-badges');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive List
|
|
||||||
|
|
||||||
<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-interactive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const selected = $(null);
|
|
||||||
const items = [
|
|
||||||
{ id: 1, name: 'Project Alpha', status: 'In Progress' },
|
|
||||||
{ id: 2, name: 'Project Beta', status: 'Planning' },
|
|
||||||
{ id: 3, name: 'Project Gamma', status: 'Completed' },
|
|
||||||
{ id: 4, name: 'Project Delta', status: 'Review' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const statusColors = {
|
|
||||||
'In Progress': 'badge-warning',
|
|
||||||
'Planning': 'badge-info',
|
|
||||||
'Completed': 'badge-success',
|
|
||||||
'Review': 'badge-accent'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({
|
|
||||||
class: `p-3 cursor-pointer transition-all hover:bg-base-200 ${selected() === item.id ? 'bg-primary/10 border-l-4 border-primary' : 'border-l-4 border-transparent'}`,
|
|
||||||
onclick: () => selected(item.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.name),
|
|
||||||
Span({ class: `badge ${statusColors[item.status]}` }, item.status)
|
|
||||||
])
|
|
||||||
])
|
|
||||||
}),
|
|
||||||
() => selected()
|
|
||||||
? Div({ class: 'alert alert-info' }, `Selected: ${items.find(i => i.id === selected()).name}`)
|
|
||||||
: Div({ class: 'alert alert-soft' }, 'Select a project to see details')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive List
|
|
||||||
|
|
||||||
<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 todos = $([
|
|
||||||
{ id: 1, text: 'Complete documentation', done: false },
|
|
||||||
{ id: 2, text: 'Review pull requests', done: false },
|
|
||||||
{ id: 3, text: 'Deploy to production', done: false }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const newTodo = $('');
|
|
||||||
|
|
||||||
const addTodo = () => {
|
|
||||||
if (newTodo().trim()) {
|
|
||||||
const newId = Math.max(...todos().map(t => t.id), 0) + 1;
|
|
||||||
todos([...todos(), { id: newId, text: newTodo(), done: false }]);
|
|
||||||
newTodo('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleTodo = (id) => {
|
|
||||||
todos(todos().map(t =>
|
|
||||||
t.id === id ? { ...t, done: !t.done } : t
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteTodo = (id) => {
|
|
||||||
todos(todos().filter(t => t.id !== id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const pendingCount = () => todos().filter(t => !t.done).length;
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Input({
|
|
||||||
placeholder: 'Add new task...',
|
|
||||||
value: newTodo,
|
|
||||||
class: 'flex-1',
|
|
||||||
oninput: (e) => newTodo(e.target.value),
|
|
||||||
onkeypress: (e) => e.key === 'Enter' && addTodo()
|
|
||||||
}),
|
|
||||||
Button({ class: 'btn btn-primary', onclick: addTodo }, 'Add')
|
|
||||||
]),
|
|
||||||
List({
|
|
||||||
items: todos,
|
|
||||||
render: (todo) => Div({ class: `flex items-center gap-3 p-2 border-b border-base-300 hover:bg-base-200 ${todo.done ? 'opacity-60' : ''}` }, [
|
|
||||||
Checkbox({
|
|
||||||
value: todo.done,
|
|
||||||
onclick: () => toggleTodo(todo.id)
|
|
||||||
}),
|
|
||||||
Span({
|
|
||||||
class: `flex-1 ${todo.done ? 'line-through' : ''}`,
|
|
||||||
onclick: () => toggleTodo(todo.id)
|
|
||||||
}, todo.text),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-xs btn-circle',
|
|
||||||
onclick: () => deleteTodo(todo.id)
|
|
||||||
}, '✕')
|
|
||||||
])
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70 mt-2' }, () => `${pendingCount()} tasks remaining`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Avatar List
|
|
||||||
|
|
||||||
<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-avatar" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AvatarDemo = () => {
|
|
||||||
const contacts = [
|
|
||||||
{ name: 'Alice Johnson', role: 'Developer', avatar: 'A', online: true },
|
|
||||||
{ name: 'Bob Smith', role: 'Designer', avatar: 'B', online: false },
|
|
||||||
{ name: 'Charlie Brown', role: 'Manager', avatar: 'C', online: true },
|
|
||||||
{ name: 'Diana Prince', role: 'QA Engineer', avatar: 'D', online: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: contacts,
|
|
||||||
render: (contact) => Div({ class: 'flex gap-3 p-3 hover:bg-base-200 transition-colors' }, [
|
|
||||||
Div({ class: `avatar ${contact.online ? 'online' : 'offline'}`, style: 'width: 48px' }, [
|
|
||||||
Div({ class: 'rounded-full bg-primary text-primary-content flex items-center justify-center w-12 h-12 font-bold' }, contact.avatar)
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, contact.name),
|
|
||||||
Div({ class: 'text-sm opacity-60' }, contact.role)
|
|
||||||
]),
|
|
||||||
Div({ class: `badge badge-sm ${contact.online ? 'badge-success' : 'badge-ghost'}` }, contact.online ? 'Online' : 'Offline')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(AvatarDemo, '#demo-avatar');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-6"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default List'),
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-2' }, item)
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Shadow'),
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-2' }, item),
|
|
||||||
class: 'shadow-lg'
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'Rounded Corners'),
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-2' }, item),
|
|
||||||
class: 'rounded-box overflow-hidden'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initListExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic List
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const items = ['Apple', 'Banana', 'Orange', 'Grape', 'Mango'];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-3 hover:bg-base-200 transition-colors' }, [
|
|
||||||
Span({ class: 'font-medium' }, item)
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Header
|
|
||||||
const headerTarget = document.querySelector('#demo-header');
|
|
||||||
if (headerTarget && !headerTarget.hasChildNodes()) {
|
|
||||||
const HeaderDemo = () => {
|
|
||||||
const users = [
|
|
||||||
{ name: 'John Doe', email: 'john@example.com', status: 'active' },
|
|
||||||
{ name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
|
|
||||||
{ name: 'Bob Johnson', email: 'bob@example.com', status: 'active' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: users,
|
|
||||||
header: Div({ class: 'p-3 bg-primary/10 font-bold border-b border-base-300' }, 'Active Users'),
|
|
||||||
render: (user) => Div({ class: 'p-3 border-b border-base-300 hover:bg-base-200' }, [
|
|
||||||
Div({ class: 'font-medium' }, user.name),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, user.email),
|
|
||||||
Span({ class: `badge badge-sm ${user.status === 'active' ? 'badge-success' : 'badge-ghost'} mt-1` }, user.status)
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(HeaderDemo, headerTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. With Icons
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const settings = [
|
|
||||||
{ icon: '🔊', label: 'Sound', description: 'Adjust volume and notifications' },
|
|
||||||
{ icon: '🌙', label: 'Display', description: 'Brightness and dark mode' },
|
|
||||||
{ icon: '🔒', label: 'Privacy', description: 'Security settings' },
|
|
||||||
{ icon: '🌐', label: 'Network', description: 'WiFi and connections' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: settings,
|
|
||||||
render: (item) => Div({ class: 'flex gap-3 p-3 hover:bg-base-200 transition-colors cursor-pointer' }, [
|
|
||||||
Div({ class: 'text-2xl' }, item.icon),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.label),
|
|
||||||
Div({ class: 'text-sm opacity-60' }, item.description)
|
|
||||||
]),
|
|
||||||
Span({ class: 'opacity-40' }, '→')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. With Badges
|
|
||||||
const badgesTarget = document.querySelector('#demo-badges');
|
|
||||||
if (badgesTarget && !badgesTarget.hasChildNodes()) {
|
|
||||||
const BadgesDemo = () => {
|
|
||||||
const notifications = [
|
|
||||||
{ id: 1, message: 'New comment on your post', time: '5 min ago', unread: true },
|
|
||||||
{ id: 2, message: 'Your order has been shipped', time: '1 hour ago', unread: true },
|
|
||||||
{ id: 3, message: 'Welcome to the platform!', time: '2 days ago', unread: false },
|
|
||||||
{ id: 4, message: 'Weekly digest available', time: '3 days ago', unread: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: notifications,
|
|
||||||
render: (item) => Div({ class: `flex justify-between items-center p-3 border-b border-base-300 hover:bg-base-200 ${item.unread ? 'bg-primary/5' : ''}` }, [
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.message),
|
|
||||||
Div({ class: 'text-xs opacity-50' }, item.time)
|
|
||||||
]),
|
|
||||||
item.unread ? Span({ class: 'badge badge-primary badge-sm' }, 'New') : null
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BadgesDemo, badgesTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Interactive List
|
|
||||||
const interactiveTarget = document.querySelector('#demo-interactive');
|
|
||||||
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const selected = $(null);
|
|
||||||
const items = [
|
|
||||||
{ id: 1, name: 'Project Alpha', status: 'In Progress' },
|
|
||||||
{ id: 2, name: 'Project Beta', status: 'Planning' },
|
|
||||||
{ id: 3, name: 'Project Gamma', status: 'Completed' },
|
|
||||||
{ id: 4, name: 'Project Delta', status: 'Review' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const statusColors = {
|
|
||||||
'In Progress': 'badge-warning',
|
|
||||||
'Planning': 'badge-info',
|
|
||||||
'Completed': 'badge-success',
|
|
||||||
'Review': 'badge-accent'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({
|
|
||||||
class: `p-3 cursor-pointer transition-all hover:bg-base-200 ${selected() === item.id ? 'bg-primary/10 border-l-4 border-primary' : 'border-l-4 border-transparent'}`,
|
|
||||||
onclick: () => selected(item.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Div({ class: 'font-medium' }, item.name),
|
|
||||||
Span({ class: `badge ${statusColors[item.status]}` }, item.status)
|
|
||||||
])
|
|
||||||
])
|
|
||||||
}),
|
|
||||||
() => selected()
|
|
||||||
? Div({ class: 'alert alert-info' }, `Selected: ${items.find(i => i.id === selected()).name}`)
|
|
||||||
: Div({ class: 'alert alert-soft' }, 'Select a project to see details')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, interactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Reactive List
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const todos = $([
|
|
||||||
{ id: 1, text: 'Complete documentation', done: false },
|
|
||||||
{ id: 2, text: 'Review pull requests', done: false },
|
|
||||||
{ id: 3, text: 'Deploy to production', done: false }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const newTodo = $('');
|
|
||||||
|
|
||||||
const addTodo = () => {
|
|
||||||
if (newTodo().trim()) {
|
|
||||||
const newId = Math.max(...todos().map(t => t.id), 0) + 1;
|
|
||||||
todos([...todos(), { id: newId, text: newTodo(), done: false }]);
|
|
||||||
newTodo('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleTodo = (id) => {
|
|
||||||
todos(todos().map(t =>
|
|
||||||
t.id === id ? { ...t, done: !t.done } : t
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteTodo = (id) => {
|
|
||||||
todos(todos().filter(t => t.id !== id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const pendingCount = () => todos().filter(t => !t.done).length;
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Input({
|
|
||||||
placeholder: 'Add new task...',
|
|
||||||
value: newTodo,
|
|
||||||
class: 'flex-1',
|
|
||||||
oninput: (e) => newTodo(e.target.value),
|
|
||||||
onkeypress: (e) => e.key === 'Enter' && addTodo()
|
|
||||||
}),
|
|
||||||
Button({ class: 'btn btn-primary', onclick: addTodo }, 'Add')
|
|
||||||
]),
|
|
||||||
List({
|
|
||||||
items: todos,
|
|
||||||
render: (todo) => Div({ class: `flex items-center gap-3 p-2 border-b border-base-300 hover:bg-base-200 ${todo.done ? 'opacity-60' : ''}` }, [
|
|
||||||
Checkbox({
|
|
||||||
value: todo.done,
|
|
||||||
onclick: () => toggleTodo(todo.id)
|
|
||||||
}),
|
|
||||||
Span({
|
|
||||||
class: `flex-1 ${todo.done ? 'line-through' : ''}`,
|
|
||||||
onclick: () => toggleTodo(todo.id)
|
|
||||||
}, todo.text),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-xs btn-circle',
|
|
||||||
onclick: () => deleteTodo(todo.id)
|
|
||||||
}, '✕')
|
|
||||||
])
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70 mt-2' }, () => `${pendingCount()} tasks remaining`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Avatar List
|
|
||||||
const avatarTarget = document.querySelector('#demo-avatar');
|
|
||||||
if (avatarTarget && !avatarTarget.hasChildNodes()) {
|
|
||||||
const AvatarDemo = () => {
|
|
||||||
const contacts = [
|
|
||||||
{ name: 'Alice Johnson', role: 'Developer', avatar: 'A', online: true },
|
|
||||||
{ name: 'Bob Smith', role: 'Designer', avatar: 'B', online: false },
|
|
||||||
{ name: 'Charlie Brown', role: 'Manager', avatar: 'C', online: true },
|
|
||||||
{ name: 'Diana Prince', role: 'QA Engineer', avatar: 'D', online: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return List({
|
|
||||||
items: contacts,
|
|
||||||
render: (contact) => Div({ class: 'flex gap-3 p-3 hover:bg-base-200 transition-colors' }, [
|
|
||||||
Div({ class: `avatar ${contact.online ? 'online' : 'offline'}`, style: 'width: 48px' }, [
|
|
||||||
Div({ class: 'rounded-full bg-primary text-primary-content flex items-center justify-center w-12 h-12 font-bold' }, contact.avatar)
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'font-medium' }, contact.name),
|
|
||||||
Div({ class: 'text-sm opacity-60' }, contact.role)
|
|
||||||
]),
|
|
||||||
Div({ class: `badge badge-sm ${contact.online ? 'badge-success' : 'badge-ghost'}` }, contact.online ? 'Online' : 'Offline')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(AvatarDemo, avatarTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const items = ['Item 1', 'Item 2', 'Item 3'];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default List'),
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-2' }, item)
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Shadow'),
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-2' }, item),
|
|
||||||
class: 'shadow-lg'
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'Rounded Corners'),
|
|
||||||
List({
|
|
||||||
items: items,
|
|
||||||
render: (item) => Div({ class: 'p-2' }, item),
|
|
||||||
class: 'rounded-box overflow-hidden'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initListExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initListExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,597 +0,0 @@
|
|||||||
# Loading
|
|
||||||
|
|
||||||
Loading spinner component for indicating loading states with customizable size and colors.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Loading`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :--------------- | :----------------------------------------------- |
|
|
||||||
| `$show` | `boolean \| Signal<boolean>` | `true` | Show/hide the loading spinner |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI loading variants)|
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Loading
|
|
||||||
|
|
||||||
<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-wrap gap-8 justify-center items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ball' }),
|
|
||||||
Loading({ $show: true, class: 'loading-bars' }),
|
|
||||||
Loading({ $show: true, class: 'loading-infinity' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Loading Sizes
|
|
||||||
|
|
||||||
<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-sizes" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-8 justify-center items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const SizesDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Extra Small')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Small')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-md' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Medium')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-lg' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Large')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SizesDemo, '#demo-sizes');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Loading Colors
|
|
||||||
|
|
||||||
<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-colors" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-8 justify-center items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ColorsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-primary' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-secondary' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-accent' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-info' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-success' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-warning' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-error' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ColorsDemo, '#demo-colors');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Button Loading 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-button" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4 items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ButtonDemo = () => {
|
|
||||||
const isLoading = $(false);
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
isLoading(true);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
isLoading(false);
|
|
||||||
Toast('Operation completed!', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
loading: isLoading,
|
|
||||||
onclick: handleClick
|
|
||||||
}, 'Submit'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Click the button to see loading state')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ButtonDemo, '#demo-button');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Async Data Loading
|
|
||||||
|
|
||||||
<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-async" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AsyncDemo = () => {
|
|
||||||
const loading = $(false);
|
|
||||||
const data = $(null);
|
|
||||||
const error = $(null);
|
|
||||||
|
|
||||||
const loadData = async () => {
|
|
||||||
loading(true);
|
|
||||||
error(null);
|
|
||||||
data(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
const result = {
|
|
||||||
users: 1234,
|
|
||||||
posts: 5678,
|
|
||||||
comments: 9012
|
|
||||||
};
|
|
||||||
data(result);
|
|
||||||
Toast('Data loaded successfully!', 'alert-success', 2000);
|
|
||||||
} catch (err) {
|
|
||||||
error('Failed to load data');
|
|
||||||
Toast('Error loading data', 'alert-error', 2000);
|
|
||||||
} finally {
|
|
||||||
loading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: loadData,
|
|
||||||
disabled: () => loading()
|
|
||||||
}, loading() ? 'Loading...' : 'Load Data')
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative min-h-[200px] flex items-center justify-center' }, [
|
|
||||||
() => loading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null,
|
|
||||||
() => data() ? Div({ class: 'grid grid-cols-3 gap-4 w-full' }, [
|
|
||||||
Div({ class: 'stat' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Users'),
|
|
||||||
Div({ class: 'stat-value' }, data().users)
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Posts'),
|
|
||||||
Div({ class: 'stat-value' }, data().posts)
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Comments'),
|
|
||||||
Div({ class: 'stat-value' }, data().comments)
|
|
||||||
])
|
|
||||||
]) : null,
|
|
||||||
() => error() ? Alert({ type: 'error', message: error() }) : null
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AsyncDemo, '#demo-async');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Full Page Loading
|
|
||||||
|
|
||||||
<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-fullpage" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const FullpageDemo = () => {
|
|
||||||
const isLoading = $(false);
|
|
||||||
|
|
||||||
const simulatePageLoad = () => {
|
|
||||||
isLoading(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
isLoading(false);
|
|
||||||
Toast('Page loaded!', 'alert-success', 2000);
|
|
||||||
}, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'relative' }, [
|
|
||||||
Div({ class: 'flex justify-center p-8' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: simulatePageLoad
|
|
||||||
}, 'Simulate Page Load')
|
|
||||||
]),
|
|
||||||
() => isLoading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FullpageDemo, '#demo-fullpage');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Conditional Loading
|
|
||||||
|
|
||||||
<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-conditional" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ConditionalDemo = () => {
|
|
||||||
const loadingState = $('idle');
|
|
||||||
const content = $('');
|
|
||||||
|
|
||||||
const simulateAction = (action) => {
|
|
||||||
loadingState('loading');
|
|
||||||
setTimeout(() => {
|
|
||||||
content(`Action: ${action} completed at ${new Date().toLocaleTimeString()}`);
|
|
||||||
loadingState('success');
|
|
||||||
setTimeout(() => loadingState('idle'), 1500);
|
|
||||||
}, 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadingStates = {
|
|
||||||
idle: null,
|
|
||||||
loading: Loading({ $show: true, class: 'loading-spinner text-primary' }),
|
|
||||||
success: Alert({ type: 'success', message: '✓ Done!' })
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => simulateAction('Save')
|
|
||||||
}, 'Save'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => simulateAction('Update')
|
|
||||||
}, 'Update'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => simulateAction('Delete')
|
|
||||||
}, 'Delete')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-center min-h-[100px] items-center' }, [
|
|
||||||
() => loadingStates[loadingState()],
|
|
||||||
() => content() && loadingState() === 'idle' ? Div({ class: 'text-center' }, content()) : null
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ConditionalDemo, '#demo-conditional');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-6"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Spinner'),
|
|
||||||
Div({ class: 'flex gap-4 justify-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-md' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Dots'),
|
|
||||||
Div({ class: 'flex gap-4 justify-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-xs' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-sm' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-md' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Ring'),
|
|
||||||
Div({ class: 'flex gap-4 justify-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-xs' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-sm' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-md' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-lg' })
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initLoadingExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Loading
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ball' }),
|
|
||||||
Loading({ $show: true, class: 'loading-bars' }),
|
|
||||||
Loading({ $show: true, class: 'loading-infinity' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Loading Sizes
|
|
||||||
const sizesTarget = document.querySelector('#demo-sizes');
|
|
||||||
if (sizesTarget && !sizesTarget.hasChildNodes()) {
|
|
||||||
const SizesDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Extra Small')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Small')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-md' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Medium')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-lg' }),
|
|
||||||
Div({ class: 'text-xs mt-2' }, 'Large')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SizesDemo, sizesTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Loading Colors
|
|
||||||
const colorsTarget = document.querySelector('#demo-colors');
|
|
||||||
if (colorsTarget && !colorsTarget.hasChildNodes()) {
|
|
||||||
const ColorsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-primary' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-secondary' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-accent' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-info' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-success' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-warning' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner text-error' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ColorsDemo, colorsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Button Loading State
|
|
||||||
const buttonTarget = document.querySelector('#demo-button');
|
|
||||||
if (buttonTarget && !buttonTarget.hasChildNodes()) {
|
|
||||||
const ButtonDemo = () => {
|
|
||||||
const isLoading = $(false);
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
isLoading(true);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
isLoading(false);
|
|
||||||
Toast('Operation completed!', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
loading: isLoading,
|
|
||||||
onclick: handleClick
|
|
||||||
}, 'Submit'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Click the button to see loading state')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ButtonDemo, buttonTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Async Data Loading
|
|
||||||
const asyncTarget = document.querySelector('#demo-async');
|
|
||||||
if (asyncTarget && !asyncTarget.hasChildNodes()) {
|
|
||||||
const AsyncDemo = () => {
|
|
||||||
const loading = $(false);
|
|
||||||
const data = $(null);
|
|
||||||
const error = $(null);
|
|
||||||
|
|
||||||
const loadData = async () => {
|
|
||||||
loading(true);
|
|
||||||
error(null);
|
|
||||||
data(null);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
||||||
const result = {
|
|
||||||
users: 1234,
|
|
||||||
posts: 5678,
|
|
||||||
comments: 9012
|
|
||||||
};
|
|
||||||
data(result);
|
|
||||||
Toast('Data loaded successfully!', 'alert-success', 2000);
|
|
||||||
} catch (err) {
|
|
||||||
error('Failed to load data');
|
|
||||||
Toast('Error loading data', 'alert-error', 2000);
|
|
||||||
} finally {
|
|
||||||
loading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: loadData,
|
|
||||||
disabled: () => loading()
|
|
||||||
}, loading() ? 'Loading...' : 'Load Data')
|
|
||||||
]),
|
|
||||||
Div({ class: 'relative min-h-[200px] flex items-center justify-center' }, [
|
|
||||||
() => loading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null,
|
|
||||||
() => data() ? Div({ class: 'grid grid-cols-3 gap-4 w-full' }, [
|
|
||||||
Div({ class: 'stat' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Users'),
|
|
||||||
Div({ class: 'stat-value' }, data().users)
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Posts'),
|
|
||||||
Div({ class: 'stat-value' }, data().posts)
|
|
||||||
]),
|
|
||||||
Div({ class: 'stat' }, [
|
|
||||||
Div({ class: 'stat-title' }, 'Comments'),
|
|
||||||
Div({ class: 'stat-value' }, data().comments)
|
|
||||||
])
|
|
||||||
]) : null,
|
|
||||||
() => error() ? Alert({ type: 'error', message: error() }) : null
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AsyncDemo, asyncTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Full Page Loading
|
|
||||||
const fullpageTarget = document.querySelector('#demo-fullpage');
|
|
||||||
if (fullpageTarget && !fullpageTarget.hasChildNodes()) {
|
|
||||||
const FullpageDemo = () => {
|
|
||||||
const isLoading = $(false);
|
|
||||||
|
|
||||||
const simulatePageLoad = () => {
|
|
||||||
isLoading(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
isLoading(false);
|
|
||||||
Toast('Page loaded!', 'alert-success', 2000);
|
|
||||||
}, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'relative' }, [
|
|
||||||
Div({ class: 'flex justify-center p-8' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: simulatePageLoad
|
|
||||||
}, 'Simulate Page Load')
|
|
||||||
]),
|
|
||||||
() => isLoading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FullpageDemo, fullpageTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Conditional Loading
|
|
||||||
const conditionalTarget = document.querySelector('#demo-conditional');
|
|
||||||
if (conditionalTarget && !conditionalTarget.hasChildNodes()) {
|
|
||||||
const ConditionalDemo = () => {
|
|
||||||
const loadingState = $('idle');
|
|
||||||
const content = $('');
|
|
||||||
|
|
||||||
const simulateAction = (action) => {
|
|
||||||
loadingState('loading');
|
|
||||||
setTimeout(() => {
|
|
||||||
content(`Action: ${action} completed at ${new Date().toLocaleTimeString()}`);
|
|
||||||
loadingState('success');
|
|
||||||
setTimeout(() => loadingState('idle'), 1500);
|
|
||||||
}, 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadingStates = {
|
|
||||||
idle: null,
|
|
||||||
loading: Loading({ $show: true, class: 'loading-spinner text-primary' }),
|
|
||||||
success: Alert({ type: 'success', message: '✓ Done!' })
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => simulateAction('Save')
|
|
||||||
}, 'Save'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => simulateAction('Update')
|
|
||||||
}, 'Update'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => simulateAction('Delete')
|
|
||||||
}, 'Delete')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-center min-h-[100px] items-center' }, [
|
|
||||||
() => loadingStates[loadingState()],
|
|
||||||
() => content() && loadingState() === 'idle' ? Div({ class: 'text-center' }, content()) : null
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ConditionalDemo, conditionalTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Spinner'),
|
|
||||||
Div({ class: 'flex gap-4 justify-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-md' }),
|
|
||||||
Loading({ $show: true, class: 'loading-spinner loading-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Dots'),
|
|
||||||
Div({ class: 'flex gap-4 justify-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-xs' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-sm' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-md' }),
|
|
||||||
Loading({ $show: true, class: 'loading-dots loading-lg' })
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Ring'),
|
|
||||||
Div({ class: 'flex gap-4 justify-center' }, [
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-xs' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-sm' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-md' }),
|
|
||||||
Loading({ $show: true, class: 'loading-ring loading-lg' })
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initLoadingExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initLoadingExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,760 +0,0 @@
|
|||||||
# Menu
|
|
||||||
|
|
||||||
Menu component for creating navigation menus, sidebars, and dropdowns with support for nested items, icons, and active states.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Menu`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `items` | `Array<MenuItem>` | `[]` | Menu items configuration |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
### MenuItem Structure
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| :---------- | :--------------------------- | :----------------------------------------------- |
|
|
||||||
| `label` | `string \| VNode` | Menu item text or content |
|
|
||||||
| `icon` | `string \| VNode` | Optional icon to display |
|
|
||||||
| `active` | `boolean \| Signal<boolean>` | Active state highlighting |
|
|
||||||
| `onclick` | `function` | Click handler |
|
|
||||||
| `children` | `Array<MenuItem>` | Nested submenu items |
|
|
||||||
| `open` | `boolean` | Whether submenu is open (for nested items) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Menu
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const activeItem = $('home');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Home',
|
|
||||||
active: () => activeItem() === 'home',
|
|
||||||
onclick: () => activeItem('home')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'About',
|
|
||||||
active: () => activeItem() === 'about',
|
|
||||||
onclick: () => activeItem('about')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Contact',
|
|
||||||
active: () => activeItem() === 'contact',
|
|
||||||
onclick: () => activeItem('contact')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Icons
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const activeItem = $('dashboard');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: '🏠',
|
|
||||||
label: 'Dashboard',
|
|
||||||
active: () => activeItem() === 'dashboard',
|
|
||||||
onclick: () => activeItem('dashboard')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📊',
|
|
||||||
label: 'Analytics',
|
|
||||||
active: () => activeItem() === 'analytics',
|
|
||||||
onclick: () => activeItem('analytics')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '⚙️',
|
|
||||||
label: 'Settings',
|
|
||||||
active: () => activeItem() === 'settings',
|
|
||||||
onclick: () => activeItem('settings')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '👤',
|
|
||||||
label: 'Profile',
|
|
||||||
active: () => activeItem() === 'profile',
|
|
||||||
onclick: () => activeItem('profile')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nested Menu
|
|
||||||
|
|
||||||
<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-nested" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const NestedDemo = () => {
|
|
||||||
const activeItem = $('products');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Dashboard',
|
|
||||||
onclick: () => activeItem('dashboard'),
|
|
||||||
active: () => activeItem() === 'dashboard'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Products',
|
|
||||||
icon: '📦',
|
|
||||||
open: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: 'All Products',
|
|
||||||
onclick: () => activeItem('all-products'),
|
|
||||||
active: () => activeItem() === 'all-products'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Add New',
|
|
||||||
onclick: () => activeItem('add-product'),
|
|
||||||
active: () => activeItem() === 'add-product'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Categories',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: 'Electronics',
|
|
||||||
onclick: () => activeItem('electronics'),
|
|
||||||
active: () => activeItem() === 'electronics'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Clothing',
|
|
||||||
onclick: () => activeItem('clothing'),
|
|
||||||
active: () => activeItem() === 'clothing'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Orders',
|
|
||||||
icon: '📋',
|
|
||||||
onclick: () => activeItem('orders'),
|
|
||||||
active: () => activeItem() === 'orders'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Settings',
|
|
||||||
icon: '⚙️',
|
|
||||||
onclick: () => activeItem('settings'),
|
|
||||||
active: () => activeItem() === 'settings'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(NestedDemo, '#demo-nested');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Horizontal Menu
|
|
||||||
|
|
||||||
<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-horizontal" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const HorizontalDemo = () => {
|
|
||||||
const activeItem = $('home');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
class: 'menu-horizontal rounded-box',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Home',
|
|
||||||
active: () => activeItem() === 'home',
|
|
||||||
onclick: () => activeItem('home')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Products',
|
|
||||||
children: [
|
|
||||||
{ label: 'Electronics', onclick: () => activeItem('electronics') },
|
|
||||||
{ label: 'Clothing', onclick: () => activeItem('clothing') },
|
|
||||||
{ label: 'Books', onclick: () => activeItem('books') }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'About',
|
|
||||||
onclick: () => activeItem('about'),
|
|
||||||
active: () => activeItem() === 'about'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Contact',
|
|
||||||
onclick: () => activeItem('contact'),
|
|
||||||
active: () => activeItem() === 'contact'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(HorizontalDemo, '#demo-horizontal');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sidebar Menu
|
|
||||||
|
|
||||||
<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-sidebar" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const SidebarDemo = () => {
|
|
||||||
const activeItem = $('dashboard');
|
|
||||||
|
|
||||||
return Div({ class: 'flex' }, [
|
|
||||||
Div({ class: 'w-64' }, [
|
|
||||||
Menu({
|
|
||||||
class: 'rounded-box w-full',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: '📊',
|
|
||||||
label: 'Dashboard',
|
|
||||||
active: () => activeItem() === 'dashboard',
|
|
||||||
onclick: () => activeItem('dashboard')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '👥',
|
|
||||||
label: 'Users',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: 'All Users',
|
|
||||||
onclick: () => activeItem('all-users'),
|
|
||||||
active: () => activeItem() === 'all-users'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Add User',
|
|
||||||
onclick: () => activeItem('add-user'),
|
|
||||||
active: () => activeItem() === 'add-user'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📁',
|
|
||||||
label: 'Files',
|
|
||||||
onclick: () => activeItem('files'),
|
|
||||||
active: () => activeItem() === 'files'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '⚙️',
|
|
||||||
label: 'Settings',
|
|
||||||
onclick: () => activeItem('settings'),
|
|
||||||
active: () => activeItem() === 'settings'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1 p-4' }, [
|
|
||||||
Div({ class: 'alert alert-info' }, () => `Current page: ${activeItem()}`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SidebarDemo, '#demo-sidebar');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Account Menu
|
|
||||||
|
|
||||||
<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-account" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AccountDemo = () => {
|
|
||||||
const [notifications, setNotifications] = $(3);
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
class: 'rounded-box w-56',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: '👤',
|
|
||||||
label: 'My Profile',
|
|
||||||
onclick: () => Toast('Profile clicked', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📧',
|
|
||||||
label: 'Messages',
|
|
||||||
badge: '3',
|
|
||||||
onclick: () => Toast('Messages opened', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🔔',
|
|
||||||
label: 'Notifications',
|
|
||||||
badge: () => notifications(),
|
|
||||||
onclick: () => {
|
|
||||||
setNotifications(0);
|
|
||||||
Toast('Notifications cleared', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '⚙️',
|
|
||||||
label: 'Settings',
|
|
||||||
onclick: () => Toast('Settings opened', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🚪',
|
|
||||||
label: 'Logout',
|
|
||||||
onclick: () => Toast('Logged out', 'alert-warning', 2000)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(AccountDemo, '#demo-account');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Collapsible Sidebar
|
|
||||||
|
|
||||||
<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-collapsible" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CollapsibleDemo = () => {
|
|
||||||
const collapsed = $(false);
|
|
||||||
const activeItem = $('dashboard');
|
|
||||||
|
|
||||||
return Div({ class: 'flex gap-4' }, [
|
|
||||||
Div({ class: `transition-all duration-300 ${collapsed() ? 'w-16' : 'w-64'}` }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm mb-2 w-full',
|
|
||||||
onclick: () => collapsed(!collapsed())
|
|
||||||
}, collapsed() ? '→' : '←'),
|
|
||||||
Menu({
|
|
||||||
class: `rounded-box ${collapsed() ? 'menu-compact' : ''}`,
|
|
||||||
items: [
|
|
||||||
{ icon: '📊', label: collapsed() ? '' : 'Dashboard', active: () => activeItem() === 'dashboard', onclick: () => activeItem('dashboard') },
|
|
||||||
{ icon: '👥', label: collapsed() ? '' : 'Users', active: () => activeItem() === 'users', onclick: () => activeItem('users') },
|
|
||||||
{ icon: '📁', label: collapsed() ? '' : 'Files', active: () => activeItem() === 'files', onclick: () => activeItem('files') },
|
|
||||||
{ icon: '⚙️', label: collapsed() ? '' : 'Settings', active: () => activeItem() === 'settings', onclick: () => activeItem('settings') }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'alert alert-info' }, () => `Selected: ${activeItem()}`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CollapsibleDemo, '#demo-collapsible');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-8"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const items = [
|
|
||||||
{ label: 'Item 1' },
|
|
||||||
{ label: 'Item 2' },
|
|
||||||
{ label: 'Item 3', children: [
|
|
||||||
{ label: 'Subitem 1' },
|
|
||||||
{ label: 'Subitem 2' }
|
|
||||||
]}
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8' }, [
|
|
||||||
Div({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Default'),
|
|
||||||
Menu({ items })
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Compact'),
|
|
||||||
Menu({ items, class: 'menu-compact' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'With Shadow'),
|
|
||||||
Menu({ items, class: 'shadow-lg' })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initMenuExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Menu
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const activeItem = $('home');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Home',
|
|
||||||
active: () => activeItem() === 'home',
|
|
||||||
onclick: () => activeItem('home')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'About',
|
|
||||||
active: () => activeItem() === 'about',
|
|
||||||
onclick: () => activeItem('about')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Contact',
|
|
||||||
active: () => activeItem() === 'contact',
|
|
||||||
onclick: () => activeItem('contact')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Icons
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const activeItem = $('dashboard');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: '🏠',
|
|
||||||
label: 'Dashboard',
|
|
||||||
active: () => activeItem() === 'dashboard',
|
|
||||||
onclick: () => activeItem('dashboard')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📊',
|
|
||||||
label: 'Analytics',
|
|
||||||
active: () => activeItem() === 'analytics',
|
|
||||||
onclick: () => activeItem('analytics')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '⚙️',
|
|
||||||
label: 'Settings',
|
|
||||||
active: () => activeItem() === 'settings',
|
|
||||||
onclick: () => activeItem('settings')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '👤',
|
|
||||||
label: 'Profile',
|
|
||||||
active: () => activeItem() === 'profile',
|
|
||||||
onclick: () => activeItem('profile')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Nested Menu
|
|
||||||
const nestedTarget = document.querySelector('#demo-nested');
|
|
||||||
if (nestedTarget && !nestedTarget.hasChildNodes()) {
|
|
||||||
const NestedDemo = () => {
|
|
||||||
const activeItem = $('products');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Dashboard',
|
|
||||||
onclick: () => activeItem('dashboard'),
|
|
||||||
active: () => activeItem() === 'dashboard'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Products',
|
|
||||||
icon: '📦',
|
|
||||||
open: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: 'All Products',
|
|
||||||
onclick: () => activeItem('all-products'),
|
|
||||||
active: () => activeItem() === 'all-products'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Add New',
|
|
||||||
onclick: () => activeItem('add-product'),
|
|
||||||
active: () => activeItem() === 'add-product'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Categories',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: 'Electronics',
|
|
||||||
onclick: () => activeItem('electronics'),
|
|
||||||
active: () => activeItem() === 'electronics'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Clothing',
|
|
||||||
onclick: () => activeItem('clothing'),
|
|
||||||
active: () => activeItem() === 'clothing'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Orders',
|
|
||||||
icon: '📋',
|
|
||||||
onclick: () => activeItem('orders'),
|
|
||||||
active: () => activeItem() === 'orders'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Settings',
|
|
||||||
icon: '⚙️',
|
|
||||||
onclick: () => activeItem('settings'),
|
|
||||||
active: () => activeItem() === 'settings'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(NestedDemo, nestedTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Horizontal Menu
|
|
||||||
const horizontalTarget = document.querySelector('#demo-horizontal');
|
|
||||||
if (horizontalTarget && !horizontalTarget.hasChildNodes()) {
|
|
||||||
const HorizontalDemo = () => {
|
|
||||||
const activeItem = $('home');
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
class: 'menu-horizontal rounded-box',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Home',
|
|
||||||
active: () => activeItem() === 'home',
|
|
||||||
onclick: () => activeItem('home')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Products',
|
|
||||||
children: [
|
|
||||||
{ label: 'Electronics', onclick: () => activeItem('electronics') },
|
|
||||||
{ label: 'Clothing', onclick: () => activeItem('clothing') },
|
|
||||||
{ label: 'Books', onclick: () => activeItem('books') }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'About',
|
|
||||||
onclick: () => activeItem('about'),
|
|
||||||
active: () => activeItem() === 'about'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Contact',
|
|
||||||
onclick: () => activeItem('contact'),
|
|
||||||
active: () => activeItem() === 'contact'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(HorizontalDemo, horizontalTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Sidebar Menu
|
|
||||||
const sidebarTarget = document.querySelector('#demo-sidebar');
|
|
||||||
if (sidebarTarget && !sidebarTarget.hasChildNodes()) {
|
|
||||||
const SidebarDemo = () => {
|
|
||||||
const activeItem = $('dashboard');
|
|
||||||
|
|
||||||
return Div({ class: 'flex' }, [
|
|
||||||
Div({ class: 'w-64' }, [
|
|
||||||
Menu({
|
|
||||||
class: 'rounded-box w-full',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: '📊',
|
|
||||||
label: 'Dashboard',
|
|
||||||
active: () => activeItem() === 'dashboard',
|
|
||||||
onclick: () => activeItem('dashboard')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '👥',
|
|
||||||
label: 'Users',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: 'All Users',
|
|
||||||
onclick: () => activeItem('all-users'),
|
|
||||||
active: () => activeItem() === 'all-users'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Add User',
|
|
||||||
onclick: () => activeItem('add-user'),
|
|
||||||
active: () => activeItem() === 'add-user'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📁',
|
|
||||||
label: 'Files',
|
|
||||||
onclick: () => activeItem('files'),
|
|
||||||
active: () => activeItem() === 'files'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '⚙️',
|
|
||||||
label: 'Settings',
|
|
||||||
onclick: () => activeItem('settings'),
|
|
||||||
active: () => activeItem() === 'settings'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1 p-4' }, [
|
|
||||||
Div({ class: 'alert alert-info' }, () => `Current page: ${activeItem()}`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SidebarDemo, sidebarTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Account Menu
|
|
||||||
const accountTarget = document.querySelector('#demo-account');
|
|
||||||
if (accountTarget && !accountTarget.hasChildNodes()) {
|
|
||||||
const AccountDemo = () => {
|
|
||||||
const [notifications, setNotifications] = $(3);
|
|
||||||
|
|
||||||
return Menu({
|
|
||||||
class: 'rounded-box w-56',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: '👤',
|
|
||||||
label: 'My Profile',
|
|
||||||
onclick: () => Toast('Profile clicked', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '📧',
|
|
||||||
label: 'Messages',
|
|
||||||
badge: '3',
|
|
||||||
onclick: () => Toast('Messages opened', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🔔',
|
|
||||||
label: 'Notifications',
|
|
||||||
badge: () => notifications(),
|
|
||||||
onclick: () => {
|
|
||||||
setNotifications(0);
|
|
||||||
Toast('Notifications cleared', 'alert-success', 2000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '⚙️',
|
|
||||||
label: 'Settings',
|
|
||||||
onclick: () => Toast('Settings opened', 'alert-info', 2000)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '🚪',
|
|
||||||
label: 'Logout',
|
|
||||||
onclick: () => Toast('Logged out', 'alert-warning', 2000)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(AccountDemo, accountTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Collapsible Sidebar
|
|
||||||
const collapsibleTarget = document.querySelector('#demo-collapsible');
|
|
||||||
if (collapsibleTarget && !collapsibleTarget.hasChildNodes()) {
|
|
||||||
const CollapsibleDemo = () => {
|
|
||||||
const collapsed = $(false);
|
|
||||||
const activeItem = $('dashboard');
|
|
||||||
|
|
||||||
return Div({ class: 'flex gap-4' }, [
|
|
||||||
Div({ class: `transition-all duration-300 ${collapsed() ? 'w-16' : 'w-64'}` }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm mb-2 w-full',
|
|
||||||
onclick: () => collapsed(!collapsed())
|
|
||||||
}, collapsed() ? '→' : '←'),
|
|
||||||
Menu({
|
|
||||||
class: `rounded-box ${collapsed() ? 'menu-compact' : ''}`,
|
|
||||||
items: [
|
|
||||||
{ icon: '📊', label: collapsed() ? '' : 'Dashboard', active: () => activeItem() === 'dashboard', onclick: () => activeItem('dashboard') },
|
|
||||||
{ icon: '👥', label: collapsed() ? '' : 'Users', active: () => activeItem() === 'users', onclick: () => activeItem('users') },
|
|
||||||
{ icon: '📁', label: collapsed() ? '' : 'Files', active: () => activeItem() === 'files', onclick: () => activeItem('files') },
|
|
||||||
{ icon: '⚙️', label: collapsed() ? '' : 'Settings', active: () => activeItem() === 'settings', onclick: () => activeItem('settings') }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex-1' }, [
|
|
||||||
Div({ class: 'alert alert-info' }, () => `Selected: ${activeItem()}`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CollapsibleDemo, collapsibleTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const items = [
|
|
||||||
{ label: 'Item 1' },
|
|
||||||
{ label: 'Item 2' },
|
|
||||||
{ label: 'Item 3', children: [
|
|
||||||
{ label: 'Subitem 1' },
|
|
||||||
{ label: 'Subitem 2' }
|
|
||||||
]}
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8' }, [
|
|
||||||
Div({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Default'),
|
|
||||||
Menu({ items })
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Compact'),
|
|
||||||
Menu({ items, class: 'menu-compact' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'With Shadow'),
|
|
||||||
Menu({ items, class: 'shadow-lg' })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initMenuExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initMenuExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,667 +0,0 @@
|
|||||||
# Modal
|
|
||||||
|
|
||||||
Modal dialog component for displaying content in an overlay with customizable actions and backdrop.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Modal`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `open` | `boolean \| Signal<boolean>` | `false` | Modal visibility state |
|
|
||||||
| `title` | `string \| VNode \| Signal` | `-` | Modal title text |
|
|
||||||
| `buttons` | `Array<VNode> \| VNode` | `-` | Action buttons to display (auto-closes on click) |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `string \| VNode` | `-` | Modal content |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Modal
|
|
||||||
|
|
||||||
<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 isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Modal'),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Basic Modal',
|
|
||||||
buttons: Button({ onclick: () => isOpen(false) }, 'Close')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4' }, 'This is a basic modal dialog. You can put any content here.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Modal with Actions
|
|
||||||
|
|
||||||
<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-actions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const confirmed = $(false);
|
|
||||||
|
|
||||||
const handleConfirm = () => {
|
|
||||||
confirmed(true);
|
|
||||||
isOpen(false);
|
|
||||||
Toast('Action confirmed!', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Confirm Action'),
|
|
||||||
() => confirmed() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: 'You confirmed the action!'
|
|
||||||
}) : null,
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Confirm Action',
|
|
||||||
buttons: [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleConfirm
|
|
||||||
}, 'Confirm')
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4' }, 'Are you sure you want to perform this action? This cannot be undone.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Form Modal
|
|
||||||
|
|
||||||
<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 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const FormModal = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const submitted = $(false);
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (name() && email()) {
|
|
||||||
submitted(true);
|
|
||||||
isOpen(false);
|
|
||||||
Toast(`Welcome ${name()}!`, 'alert-success', 2000);
|
|
||||||
setTimeout(() => submitted(false), 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Sign Up'),
|
|
||||||
() => submitted() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: 'Account created successfully!'
|
|
||||||
}) : null,
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Create Account',
|
|
||||||
buttons: [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Sign Up')
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
Div({ class: 'flex flex-col gap-4 py-2' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Full Name',
|
|
||||||
value: name,
|
|
||||||
placeholder: 'Enter your name',
|
|
||||||
oninput: (e) => name(e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'Enter your email',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormModal, '#demo-form');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Confirmation Modal
|
|
||||||
|
|
||||||
<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-confirm" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ConfirmDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const items = $([
|
|
||||||
{ id: 1, name: 'Document A' },
|
|
||||||
{ id: 2, name: 'Document B' },
|
|
||||||
{ id: 3, name: 'Document C' }
|
|
||||||
]);
|
|
||||||
const pendingDelete = $(null);
|
|
||||||
|
|
||||||
const confirmDelete = (item) => {
|
|
||||||
pendingDelete(item);
|
|
||||||
isOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
items(items().filter(i => i.id !== pendingDelete().id));
|
|
||||||
isOpen(false);
|
|
||||||
Toast(`Deleted: ${pendingDelete().name}`, 'alert-info', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
|
|
||||||
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({}, item.name),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-error',
|
|
||||||
onclick: () => confirmDelete(item)
|
|
||||||
}, 'Delete')
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Delete Confirmation',
|
|
||||||
buttons: [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: handleDelete
|
|
||||||
}, 'Delete')
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4' }, () => `Are you sure you want to delete "${pendingDelete()?.name}"? This action cannot be undone.`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ConfirmDemo, '#demo-confirm');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Large Content Modal
|
|
||||||
|
|
||||||
<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-large" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const LargeDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Large Modal'),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Terms and Conditions',
|
|
||||||
buttons: Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'I Agree')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4 max-h-96 overflow-y-auto' }, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '1. Introduction'),
|
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '2. User Obligations'),
|
|
||||||
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '3. Privacy Policy'),
|
|
||||||
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '4. Termination'),
|
|
||||||
'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores.'
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(LargeDemo, '#demo-large');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple Modals
|
|
||||||
|
|
||||||
<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 flex flex-wrap gap-4 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const MultipleDemo = () => {
|
|
||||||
const modal1 = $(false);
|
|
||||||
const modal2 = $(false);
|
|
||||||
const modal3 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Button({ class: 'btn', onclick: () => modal1(true) }, 'Info Modal'),
|
|
||||||
Button({ class: 'btn', onclick: () => modal2(true) }, 'Success Modal'),
|
|
||||||
Button({ class: 'btn', onclick: () => modal3(true) }, 'Warning Modal'),
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
open: modal1,
|
|
||||||
title: 'Information',
|
|
||||||
buttons: Button({ onclick: () => modal1(false) }, 'OK')
|
|
||||||
}, 'This is an informational message.'),
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
open: modal2,
|
|
||||||
title: 'Success!',
|
|
||||||
buttons: Button({ onclick: () => modal2(false) }, 'Great!')
|
|
||||||
}, 'Your operation was completed successfully.'),
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
open: modal3,
|
|
||||||
title: 'Warning',
|
|
||||||
buttons: Button({ onclick: () => modal3(false) }, 'Understood')
|
|
||||||
}, 'Please review your input before proceeding.')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MultipleDemo, '#demo-multiple');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Styled Modal
|
|
||||||
|
|
||||||
<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-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Custom Modal'),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
class: 'bg-gradient-to-r from-primary to-secondary text-primary-content'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'text-center py-8' }, [
|
|
||||||
Div({ class: 'text-6xl mb-4' }, '🎉'),
|
|
||||||
Div({ class: 'text-2xl font-bold mb-2' }, 'Congratulations!'),
|
|
||||||
Div({ class: 'mb-6' }, 'You\'ve successfully completed the tutorial.'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost bg-white/20 hover:bg-white/30',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Get Started')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, '#demo-custom');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initModalExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Modal
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Modal'),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Basic Modal',
|
|
||||||
buttons: Button({ onclick: () => isOpen(false) }, 'Close')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4' }, 'This is a basic modal dialog. You can put any content here.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Modal with Actions
|
|
||||||
const actionsTarget = document.querySelector('#demo-actions');
|
|
||||||
if (actionsTarget && !actionsTarget.hasChildNodes()) {
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const confirmed = $(false);
|
|
||||||
|
|
||||||
const handleConfirm = () => {
|
|
||||||
confirmed(true);
|
|
||||||
isOpen(false);
|
|
||||||
Toast('Action confirmed!', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Confirm Action'),
|
|
||||||
() => confirmed() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: 'You confirmed the action!'
|
|
||||||
}) : null,
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Confirm Action',
|
|
||||||
buttons: [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleConfirm
|
|
||||||
}, 'Confirm')
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4' }, 'Are you sure you want to perform this action? This cannot be undone.')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, actionsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Form Modal
|
|
||||||
const formTarget = document.querySelector('#demo-form');
|
|
||||||
if (formTarget && !formTarget.hasChildNodes()) {
|
|
||||||
const FormModal = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const name = $('');
|
|
||||||
const email = $('');
|
|
||||||
const submitted = $(false);
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (name() && email()) {
|
|
||||||
submitted(true);
|
|
||||||
isOpen(false);
|
|
||||||
Toast(`Welcome ${name()}!`, 'alert-success', 2000);
|
|
||||||
setTimeout(() => submitted(false), 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Sign Up'),
|
|
||||||
() => submitted() ? Alert({
|
|
||||||
type: 'success',
|
|
||||||
message: 'Account created successfully!'
|
|
||||||
}) : null,
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Create Account',
|
|
||||||
buttons: [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Sign Up')
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
Div({ class: 'flex flex-col gap-4 py-2' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Full Name',
|
|
||||||
value: name,
|
|
||||||
placeholder: 'Enter your name',
|
|
||||||
oninput: (e) => name(e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'Enter your email',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormModal, formTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Confirmation Modal
|
|
||||||
const confirmTarget = document.querySelector('#demo-confirm');
|
|
||||||
if (confirmTarget && !confirmTarget.hasChildNodes()) {
|
|
||||||
const ConfirmDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
const items = $([
|
|
||||||
{ id: 1, name: 'Document A' },
|
|
||||||
{ id: 2, name: 'Document B' },
|
|
||||||
{ id: 3, name: 'Document C' }
|
|
||||||
]);
|
|
||||||
const pendingDelete = $(null);
|
|
||||||
|
|
||||||
const confirmDelete = (item) => {
|
|
||||||
pendingDelete(item);
|
|
||||||
isOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
items(items().filter(i => i.id !== pendingDelete().id));
|
|
||||||
isOpen(false);
|
|
||||||
Toast(`Deleted: ${pendingDelete().name}`, 'alert-info', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
|
|
||||||
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({}, item.name),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-error',
|
|
||||||
onclick: () => confirmDelete(item)
|
|
||||||
}, 'Delete')
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Delete Confirmation',
|
|
||||||
buttons: [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Cancel'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: handleDelete
|
|
||||||
}, 'Delete')
|
|
||||||
]
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4' }, () => `Are you sure you want to delete "${pendingDelete()?.name}"? This action cannot be undone.`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ConfirmDemo, confirmTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Large Content Modal
|
|
||||||
const largeTarget = document.querySelector('#demo-large');
|
|
||||||
if (largeTarget && !largeTarget.hasChildNodes()) {
|
|
||||||
const LargeDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Large Modal'),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
title: 'Terms and Conditions',
|
|
||||||
buttons: Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'I Agree')
|
|
||||||
}, [
|
|
||||||
Div({ class: 'py-4 max-h-96 overflow-y-auto' }, [
|
|
||||||
Div({ class: 'space-y-4' }, [
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '1. Introduction'),
|
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '2. User Obligations'),
|
|
||||||
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '3. Privacy Policy'),
|
|
||||||
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.'
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-sm' }, [
|
|
||||||
Div({ class: 'font-bold mb-2' }, '4. Termination'),
|
|
||||||
'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores.'
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(LargeDemo, largeTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Multiple Modals
|
|
||||||
const multipleTarget = document.querySelector('#demo-multiple');
|
|
||||||
if (multipleTarget && !multipleTarget.hasChildNodes()) {
|
|
||||||
const MultipleDemo = () => {
|
|
||||||
const modal1 = $(false);
|
|
||||||
const modal2 = $(false);
|
|
||||||
const modal3 = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Button({ class: 'btn', onclick: () => modal1(true) }, 'Info Modal'),
|
|
||||||
Button({ class: 'btn', onclick: () => modal2(true) }, 'Success Modal'),
|
|
||||||
Button({ class: 'btn', onclick: () => modal3(true) }, 'Warning Modal'),
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
open: modal1,
|
|
||||||
title: 'Information',
|
|
||||||
buttons: Button({ onclick: () => modal1(false) }, 'OK')
|
|
||||||
}, 'This is an informational message.'),
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
open: modal2,
|
|
||||||
title: 'Success!',
|
|
||||||
buttons: Button({ onclick: () => modal2(false) }, 'Great!')
|
|
||||||
}, 'Your operation was completed successfully.'),
|
|
||||||
|
|
||||||
Modal({
|
|
||||||
open: modal3,
|
|
||||||
title: 'Warning',
|
|
||||||
buttons: Button({ onclick: () => modal3(false) }, 'Understood')
|
|
||||||
}, 'Please review your input before proceeding.')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MultipleDemo, multipleTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Custom Styled Modal
|
|
||||||
const customTarget = document.querySelector('#demo-custom');
|
|
||||||
if (customTarget && !customTarget.hasChildNodes()) {
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => isOpen(true)
|
|
||||||
}, 'Open Custom Modal'),
|
|
||||||
Modal({
|
|
||||||
open: isOpen,
|
|
||||||
class: 'bg-gradient-to-r from-primary to-secondary text-primary-content'
|
|
||||||
}, [
|
|
||||||
Div({ class: 'text-center py-8' }, [
|
|
||||||
Div({ class: 'text-6xl mb-4' }, '🎉'),
|
|
||||||
Div({ class: 'text-2xl font-bold mb-2' }, 'Congratulations!'),
|
|
||||||
Div({ class: 'mb-6' }, 'You\'ve successfully completed the tutorial.'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost bg-white/20 hover:bg-white/30',
|
|
||||||
onclick: () => isOpen(false)
|
|
||||||
}, 'Get Started')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, customTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initModalExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initModalExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,551 +0,0 @@
|
|||||||
# Navbar
|
|
||||||
|
|
||||||
Navigation bar component for creating responsive headers with logo, navigation links, and actions.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Navbar`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `VNode \| Array<VNode>` | `-` | Navbar content (should contain left, center, right sections) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Navbar
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Logo')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Navigation Items')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm' }, 'Login')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Navigation Links
|
|
||||||
|
|
||||||
<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-links" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const LinksDemo = () => {
|
|
||||||
const active = $('home');
|
|
||||||
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost btn-sm ${active() === 'home' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => active('home')
|
|
||||||
}, 'Home'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost btn-sm ${active() === 'about' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => active('about')
|
|
||||||
}, 'About'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost btn-sm ${active() === 'contact' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => active('contact')
|
|
||||||
}, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-primary btn-sm' }, 'Sign Up')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(LinksDemo, '#demo-links');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Dropdown Menu
|
|
||||||
|
|
||||||
<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-dropdown" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DropdownDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Div({ class: 'dropdown' }, [
|
|
||||||
Div({ tabindex: 0, role: 'button', class: 'btn btn-ghost lg:hidden' }, [
|
|
||||||
Span({ class: 'sr-only' }, 'Open menu'),
|
|
||||||
Icons.iconInfo
|
|
||||||
]),
|
|
||||||
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Home'),
|
|
||||||
Div({ class: 'menu-item' }, 'About'),
|
|
||||||
Div({ class: 'menu-item' }, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Home'),
|
|
||||||
Div({ class: 'menu-item' }, 'About'),
|
|
||||||
Div({ class: 'menu-item' }, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-primary btn-sm' }, 'Login')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DropdownDemo, '#demo-dropdown');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Search
|
|
||||||
|
|
||||||
<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-search" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const SearchDemo = () => {
|
|
||||||
const searchQuery = $('');
|
|
||||||
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center' }, [
|
|
||||||
Div({ class: 'form-control' }, [
|
|
||||||
Input({
|
|
||||||
placeholder: 'Search...',
|
|
||||||
value: searchQuery,
|
|
||||||
class: 'input input-bordered w-48 md:w-auto',
|
|
||||||
oninput: (e) => searchQuery(e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '🔔'),
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '👤')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SearchDemo, '#demo-search');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Avatar and Dropdown
|
|
||||||
|
|
||||||
<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-avatar" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AvatarDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Dashboard'),
|
|
||||||
Div({ class: 'menu-item' }, 'Projects'),
|
|
||||||
Div({ class: 'menu-item' }, 'Tasks')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Div({ class: 'dropdown dropdown-end' }, [
|
|
||||||
Div({ tabindex: 0, role: 'button', class: 'btn btn-ghost btn-circle avatar' }, [
|
|
||||||
Div({ class: 'w-8 rounded-full bg-primary text-primary-content flex items-center justify-center' }, 'JD')
|
|
||||||
]),
|
|
||||||
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Profile'),
|
|
||||||
Div({ class: 'menu-item' }, 'Settings'),
|
|
||||||
Div({ class: 'menu-item' }, 'Logout')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AvatarDemo, '#demo-avatar');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Responsive Navbar
|
|
||||||
|
|
||||||
<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-responsive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ResponsiveDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col' }, [
|
|
||||||
Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle lg:hidden',
|
|
||||||
onclick: () => isOpen(!isOpen())
|
|
||||||
}, '☰')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '🔔')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
() => isOpen() ? Div({ class: 'flex flex-col gap-2 p-4 bg-base-200 rounded-box mt-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Home'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'About'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Services'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Contact')
|
|
||||||
]) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ResponsiveDemo, '#demo-responsive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Brand and Actions
|
|
||||||
|
|
||||||
<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-brand" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BrandDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box bg-primary text-primary-content' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Brand')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Features'),
|
|
||||||
Span({ class: 'text-sm' }, 'Pricing'),
|
|
||||||
Span({ class: 'text-sm' }, 'About')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm' }, 'Login'),
|
|
||||||
Button({ class: 'btn btn-outline btn-sm ml-2' }, 'Sign Up')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BrandDemo, '#demo-brand');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Navbar'),
|
|
||||||
Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [Span({}, 'Start')]),
|
|
||||||
Div({ class: 'navbar-center' }, [Span({}, 'Center')]),
|
|
||||||
Div({ class: 'navbar-end' }, [Span({}, 'End')])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Shadow'),
|
|
||||||
Navbar({ class: 'rounded-box shadow-lg' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [Span({}, 'Logo')]),
|
|
||||||
Div({ class: 'navbar-end' }, [Button({ class: 'btn btn-sm' }, 'Button')])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Background'),
|
|
||||||
Navbar({ class: 'rounded-box bg-base-300' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [Span({ class: 'font-bold' }, 'Colored')]),
|
|
||||||
Div({ class: 'navbar-end' }, [Span({ class: 'text-sm' }, 'Info')])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initNavbarExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Navbar
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Logo')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Navigation Items')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm' }, 'Login')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Navigation Links
|
|
||||||
const linksTarget = document.querySelector('#demo-links');
|
|
||||||
if (linksTarget && !linksTarget.hasChildNodes()) {
|
|
||||||
const LinksDemo = () => {
|
|
||||||
const active = $('home');
|
|
||||||
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost btn-sm ${active() === 'home' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => active('home')
|
|
||||||
}, 'Home'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost btn-sm ${active() === 'about' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => active('about')
|
|
||||||
}, 'About'),
|
|
||||||
Button({
|
|
||||||
class: `btn btn-ghost btn-sm ${active() === 'contact' ? 'btn-active' : ''}`,
|
|
||||||
onclick: () => active('contact')
|
|
||||||
}, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-primary btn-sm' }, 'Sign Up')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(LinksDemo, linksTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. With Dropdown Menu
|
|
||||||
const dropdownTarget = document.querySelector('#demo-dropdown');
|
|
||||||
if (dropdownTarget && !dropdownTarget.hasChildNodes()) {
|
|
||||||
const DropdownDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Div({ class: 'dropdown' }, [
|
|
||||||
Div({ tabindex: 0, role: 'button', class: 'btn btn-ghost lg:hidden' }, [
|
|
||||||
Span({ class: 'sr-only' }, 'Open menu'),
|
|
||||||
Icons.iconInfo
|
|
||||||
]),
|
|
||||||
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Home'),
|
|
||||||
Div({ class: 'menu-item' }, 'About'),
|
|
||||||
Div({ class: 'menu-item' }, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Home'),
|
|
||||||
Div({ class: 'menu-item' }, 'About'),
|
|
||||||
Div({ class: 'menu-item' }, 'Contact')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-primary btn-sm' }, 'Login')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DropdownDemo, dropdownTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. With Search
|
|
||||||
const searchTarget = document.querySelector('#demo-search');
|
|
||||||
if (searchTarget && !searchTarget.hasChildNodes()) {
|
|
||||||
const SearchDemo = () => {
|
|
||||||
const searchQuery = $('');
|
|
||||||
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center' }, [
|
|
||||||
Div({ class: 'form-control' }, [
|
|
||||||
Input({
|
|
||||||
placeholder: 'Search...',
|
|
||||||
value: searchQuery,
|
|
||||||
class: 'input input-bordered w-48 md:w-auto',
|
|
||||||
oninput: (e) => searchQuery(e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '🔔'),
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '👤')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(SearchDemo, searchTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. With Avatar and Dropdown
|
|
||||||
const avatarTarget = document.querySelector('#demo-avatar');
|
|
||||||
if (avatarTarget && !avatarTarget.hasChildNodes()) {
|
|
||||||
const AvatarDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Dashboard'),
|
|
||||||
Div({ class: 'menu-item' }, 'Projects'),
|
|
||||||
Div({ class: 'menu-item' }, 'Tasks')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Div({ class: 'dropdown dropdown-end' }, [
|
|
||||||
Div({ tabindex: 0, role: 'button', class: 'btn btn-ghost btn-circle avatar' }, [
|
|
||||||
Div({ class: 'w-8 rounded-full bg-primary text-primary-content flex items-center justify-center' }, 'JD')
|
|
||||||
]),
|
|
||||||
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
|
|
||||||
Div({ class: 'menu-item' }, 'Profile'),
|
|
||||||
Div({ class: 'menu-item' }, 'Settings'),
|
|
||||||
Div({ class: 'menu-item' }, 'Logout')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AvatarDemo, avatarTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Responsive Navbar
|
|
||||||
const responsiveTarget = document.querySelector('#demo-responsive');
|
|
||||||
if (responsiveTarget && !responsiveTarget.hasChildNodes()) {
|
|
||||||
const ResponsiveDemo = () => {
|
|
||||||
const isOpen = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col' }, [
|
|
||||||
Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-circle lg:hidden',
|
|
||||||
onclick: () => isOpen(!isOpen())
|
|
||||||
}, '☰')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'MyApp')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '🔔')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
() => isOpen() ? Div({ class: 'flex flex-col gap-2 p-4 bg-base-200 rounded-box mt-2' }, [
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Home'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'About'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Services'),
|
|
||||||
Button({ class: 'btn btn-ghost justify-start' }, 'Contact')
|
|
||||||
]) : null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ResponsiveDemo, responsiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. With Brand and Actions
|
|
||||||
const brandTarget = document.querySelector('#demo-brand');
|
|
||||||
if (brandTarget && !brandTarget.hasChildNodes()) {
|
|
||||||
const BrandDemo = () => {
|
|
||||||
return Navbar({ class: 'rounded-box bg-primary text-primary-content' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [
|
|
||||||
Span({ class: 'text-xl font-bold' }, 'Brand')
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-center hidden lg:flex' }, [
|
|
||||||
Div({ class: 'menu menu-horizontal px-1' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Features'),
|
|
||||||
Span({ class: 'text-sm' }, 'Pricing'),
|
|
||||||
Span({ class: 'text-sm' }, 'About')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'navbar-end' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-sm' }, 'Login'),
|
|
||||||
Button({ class: 'btn btn-outline btn-sm ml-2' }, 'Sign Up')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BrandDemo, brandTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Navbar'),
|
|
||||||
Navbar({ class: 'rounded-box' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [Span({}, 'Start')]),
|
|
||||||
Div({ class: 'navbar-center' }, [Span({}, 'Center')]),
|
|
||||||
Div({ class: 'navbar-end' }, [Span({}, 'End')])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Shadow'),
|
|
||||||
Navbar({ class: 'rounded-box shadow-lg' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [Span({}, 'Logo')]),
|
|
||||||
Div({ class: 'navbar-end' }, [Button({ class: 'btn btn-sm' }, 'Button')])
|
|
||||||
]),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-2' }, 'With Background'),
|
|
||||||
Navbar({ class: 'rounded-box bg-base-300' }, [
|
|
||||||
Div({ class: 'navbar-start' }, [Span({ class: 'font-bold' }, 'Colored')]),
|
|
||||||
Div({ class: 'navbar-end' }, [Span({ class: 'text-sm' }, 'Info')])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initNavbarExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initNavbarExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,740 +0,0 @@
|
|||||||
# Radio
|
|
||||||
|
|
||||||
Radio button component with label, tooltip support, and reactive group selection. All radios in the same group share a common `name` attribute for proper HTML semantics.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Radio`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `label` | `string` | `-` | Label text for the radio button |
|
|
||||||
| `value` | `string \| Signal<string>` | `-` | Selected value signal for the group |
|
|
||||||
| `radioValue` | `string` | `-` | Value of this radio button |
|
|
||||||
| `name` | `string` | `-` | Group name (all radios in group should share this) |
|
|
||||||
| `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',
|
|
||||||
name: 'basic-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'option1',
|
|
||||||
onclick: () => selected('option1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option 2',
|
|
||||||
name: 'basic-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'option2',
|
|
||||||
onclick: () => selected('option2')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option 3',
|
|
||||||
name: 'basic-group',
|
|
||||||
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',
|
|
||||||
name: 'theme-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'light',
|
|
||||||
tooltip: 'Light theme with white background',
|
|
||||||
onclick: () => selected('light')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Dark mode',
|
|
||||||
name: 'theme-group',
|
|
||||||
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',
|
|
||||||
name: 'disabled-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'enabled',
|
|
||||||
onclick: () => selected('enabled')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Disabled option (cannot select)',
|
|
||||||
name: 'disabled-group',
|
|
||||||
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' },
|
|
||||||
{ value: 'green', label: 'Green' },
|
|
||||||
{ value: 'red', label: 'Red' }
|
|
||||||
];
|
|
||||||
|
|
||||||
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,
|
|
||||||
name: 'size-group',
|
|
||||||
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,
|
|
||||||
name: 'color-group',
|
|
||||||
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',
|
|
||||||
name: 'payment-group',
|
|
||||||
value: method,
|
|
||||||
radioValue: 'credit',
|
|
||||||
onclick: () => method('credit')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: '🏦 Bank Transfer',
|
|
||||||
name: 'payment-group',
|
|
||||||
value: method,
|
|
||||||
radioValue: 'bank',
|
|
||||||
onclick: () => method('bank')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: '📱 Digital Wallet',
|
|
||||||
name: 'payment-group',
|
|
||||||
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',
|
|
||||||
name: 'primary-group',
|
|
||||||
value: primary,
|
|
||||||
radioValue: 'primary1',
|
|
||||||
class: 'radio-primary',
|
|
||||||
onclick: () => primary('primary1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option B',
|
|
||||||
name: 'primary-group',
|
|
||||||
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',
|
|
||||||
name: 'secondary-group',
|
|
||||||
value: secondary,
|
|
||||||
radioValue: 'secondary1',
|
|
||||||
class: 'radio-secondary',
|
|
||||||
onclick: () => secondary('secondary1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option B',
|
|
||||||
name: 'secondary-group',
|
|
||||||
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',
|
|
||||||
name: 'accent-group',
|
|
||||||
value: accent,
|
|
||||||
radioValue: 'accent1',
|
|
||||||
class: 'radio-accent',
|
|
||||||
onclick: () => accent('accent1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option B',
|
|
||||||
name: 'accent-group',
|
|
||||||
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',
|
|
||||||
name: 'category-group',
|
|
||||||
value: category,
|
|
||||||
radioValue: 'cars',
|
|
||||||
onclick: () => {
|
|
||||||
category('cars');
|
|
||||||
selected('');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Colors',
|
|
||||||
name: 'category-group',
|
|
||||||
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,
|
|
||||||
name: 'dynamic-option-group',
|
|
||||||
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',
|
|
||||||
name: 'basic-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'option1',
|
|
||||||
onclick: () => selected('option1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option 2',
|
|
||||||
name: 'basic-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'option2',
|
|
||||||
onclick: () => selected('option2')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option 3',
|
|
||||||
name: 'basic-group',
|
|
||||||
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',
|
|
||||||
name: 'theme-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'light',
|
|
||||||
tooltip: 'Light theme with white background',
|
|
||||||
onclick: () => selected('light')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Dark mode',
|
|
||||||
name: 'theme-group',
|
|
||||||
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',
|
|
||||||
name: 'disabled-group',
|
|
||||||
value: selected,
|
|
||||||
radioValue: 'enabled',
|
|
||||||
onclick: () => selected('enabled')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Disabled option (cannot select)',
|
|
||||||
name: 'disabled-group',
|
|
||||||
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' },
|
|
||||||
{ value: 'green', label: 'Green' },
|
|
||||||
{ value: 'red', label: 'Red' }
|
|
||||||
];
|
|
||||||
|
|
||||||
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,
|
|
||||||
name: 'size-group',
|
|
||||||
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,
|
|
||||||
name: 'color-group',
|
|
||||||
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',
|
|
||||||
name: 'payment-group',
|
|
||||||
value: method,
|
|
||||||
radioValue: 'credit',
|
|
||||||
onclick: () => method('credit')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: '🏦 Bank Transfer',
|
|
||||||
name: 'payment-group',
|
|
||||||
value: method,
|
|
||||||
radioValue: 'bank',
|
|
||||||
onclick: () => method('bank')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: '📱 Digital Wallet',
|
|
||||||
name: 'payment-group',
|
|
||||||
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',
|
|
||||||
name: 'primary-group',
|
|
||||||
value: primary,
|
|
||||||
radioValue: 'primary1',
|
|
||||||
class: 'radio-primary',
|
|
||||||
onclick: () => primary('primary1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option B',
|
|
||||||
name: 'primary-group',
|
|
||||||
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',
|
|
||||||
name: 'secondary-group',
|
|
||||||
value: secondary,
|
|
||||||
radioValue: 'secondary1',
|
|
||||||
class: 'radio-secondary',
|
|
||||||
onclick: () => secondary('secondary1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option B',
|
|
||||||
name: 'secondary-group',
|
|
||||||
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',
|
|
||||||
name: 'accent-group',
|
|
||||||
value: accent,
|
|
||||||
radioValue: 'accent1',
|
|
||||||
class: 'radio-accent',
|
|
||||||
onclick: () => accent('accent1')
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Option B',
|
|
||||||
name: 'accent-group',
|
|
||||||
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',
|
|
||||||
name: 'category-group',
|
|
||||||
value: category,
|
|
||||||
radioValue: 'cars',
|
|
||||||
onclick: () => {
|
|
||||||
category('cars');
|
|
||||||
selected('');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Radio({
|
|
||||||
label: 'Colors',
|
|
||||||
name: 'category-group',
|
|
||||||
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,
|
|
||||||
name: 'dynamic-option-group',
|
|
||||||
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>
|
|
||||||
@@ -1,536 +0,0 @@
|
|||||||
# Rating
|
|
||||||
|
|
||||||
Star rating component with customizable count, icons, and read-only mode.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Rating`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :--------------- | :----------------------------------------------- |
|
|
||||||
| `value` | `number \| Signal<number>` | `0` | Current rating value |
|
|
||||||
| `count` | `number \| Signal<number>` | `5` | Number of stars/items |
|
|
||||||
| `mask` | `string` | `'mask-star'` | Mask shape (mask-star, mask-star-2, mask-heart) |
|
|
||||||
| `readonly` | `boolean \| Signal<boolean>` | `false` | Disable interaction |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Rating
|
|
||||||
|
|
||||||
<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 rating = $(3);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / 5`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Heart Rating
|
|
||||||
|
|
||||||
<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-heart" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const HeartDemo = () => {
|
|
||||||
const rating = $(4);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
mask: 'mask-heart',
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `${rating()} hearts`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(HeartDemo, '#demo-heart');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Star with Outline
|
|
||||||
|
|
||||||
<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-star2" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const Star2Demo = () => {
|
|
||||||
const rating = $(2);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
mask: 'mask-star-2',
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `${rating()} stars`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(Star2Demo, '#demo-star2');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read-only Rating
|
|
||||||
|
|
||||||
<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-readonly" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ReadonlyDemo = () => {
|
|
||||||
const rating = $(4.5);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
readonly: true
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Average rating: 4.5/5 (read-only)')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReadonlyDemo, '#demo-readonly');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Count
|
|
||||||
|
|
||||||
<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-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const rating = $(3);
|
|
||||||
const count = $(10);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 w-full' }, [
|
|
||||||
Div({ class: 'flex items-center gap-4' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Number of stars:'),
|
|
||||||
Input({
|
|
||||||
type: 'number',
|
|
||||||
value: count,
|
|
||||||
class: 'input input-sm w-24',
|
|
||||||
oninput: (e) => count(parseInt(e.target.value) || 1)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: count,
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / ${count()}`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, '#demo-custom');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Product Review
|
|
||||||
|
|
||||||
<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-review" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ReviewDemo = () => {
|
|
||||||
const quality = $(4);
|
|
||||||
const price = $(3);
|
|
||||||
const support = $(5);
|
|
||||||
|
|
||||||
const average = () => Math.round(((quality() + price() + support()) / 3) * 10) / 10;
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'text-lg font-bold' }, 'Product Review'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm w-24' }, 'Quality:'),
|
|
||||||
Rating({
|
|
||||||
value: quality,
|
|
||||||
count: 5,
|
|
||||||
size: 'sm',
|
|
||||||
onchange: (v) => quality(v)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm w-24' }, 'Price:'),
|
|
||||||
Rating({
|
|
||||||
value: price,
|
|
||||||
count: 5,
|
|
||||||
size: 'sm',
|
|
||||||
onchange: (v) => price(v)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm w-24' }, 'Support:'),
|
|
||||||
Rating({
|
|
||||||
value: support,
|
|
||||||
count: 5,
|
|
||||||
size: 'sm',
|
|
||||||
onchange: (v) => support(v)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'divider my-1' }),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'font-bold' }, 'Overall:'),
|
|
||||||
Div({ class: 'text-2xl font-bold text-primary' }, () => average())
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReviewDemo, '#demo-review');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-6"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Mask Star'),
|
|
||||||
Rating({ value: $(3), count: 5, mask: 'mask-star' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Mask Star 2 (yellow)' ),
|
|
||||||
Rating({ value: $(4), count: 5, mask: 'mask-star-2', class: 'rating-warning' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Mask Heart'),
|
|
||||||
Rating({ value: $(5), count: 5, mask: 'mask-heart', class: 'rating-error' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Half Stars (read-only)'),
|
|
||||||
Rating({ value: $(3.5), count: 5, readonly: true })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive Feedback
|
|
||||||
|
|
||||||
<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-feedback" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const FeedbackDemo = () => {
|
|
||||||
const rating = $(0);
|
|
||||||
const feedback = $(false);
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
1: 'Very disappointed 😞',
|
|
||||||
2: 'Could be better 😕',
|
|
||||||
3: 'Good 👍',
|
|
||||||
4: 'Very good 😊',
|
|
||||||
5: 'Excellent! 🎉'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'How was your experience?'),
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
onchange: (value) => {
|
|
||||||
rating(value);
|
|
||||||
feedback(true);
|
|
||||||
if (value >= 4) {
|
|
||||||
Toast('Thank you for your positive feedback!', 'alert-success', 2000);
|
|
||||||
} else if (value <= 2) {
|
|
||||||
Toast('We appreciate your feedback and will improve!', 'alert-warning', 2000);
|
|
||||||
} else {
|
|
||||||
Toast('Thanks for your rating!', 'alert-info', 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
() => rating() > 0
|
|
||||||
? Div({ class: 'alert alert-soft text-center' }, [
|
|
||||||
messages[rating()] || `Rating: ${rating()} stars`
|
|
||||||
])
|
|
||||||
: null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FeedbackDemo, '#demo-feedback');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initRatingExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Rating
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const rating = $(3);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / 5`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Heart Rating
|
|
||||||
const heartTarget = document.querySelector('#demo-heart');
|
|
||||||
if (heartTarget && !heartTarget.hasChildNodes()) {
|
|
||||||
const HeartDemo = () => {
|
|
||||||
const rating = $(4);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
mask: 'mask-heart',
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `${rating()} hearts`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(HeartDemo, heartTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Star with Outline
|
|
||||||
const star2Target = document.querySelector('#demo-star2');
|
|
||||||
if (star2Target && !star2Target.hasChildNodes()) {
|
|
||||||
const Star2Demo = () => {
|
|
||||||
const rating = $(2);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
mask: 'mask-star-2',
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `${rating()} stars`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(Star2Demo, star2Target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Read-only Rating
|
|
||||||
const readonlyTarget = document.querySelector('#demo-readonly');
|
|
||||||
if (readonlyTarget && !readonlyTarget.hasChildNodes()) {
|
|
||||||
const ReadonlyDemo = () => {
|
|
||||||
const rating = $(4.5);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-2 items-center' }, [
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
readonly: true
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, 'Average rating: 4.5/5 (read-only)')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReadonlyDemo, readonlyTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Custom Count
|
|
||||||
const customTarget = document.querySelector('#demo-custom');
|
|
||||||
if (customTarget && !customTarget.hasChildNodes()) {
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const rating = $(3);
|
|
||||||
const count = $(10);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 w-full' }, [
|
|
||||||
Div({ class: 'flex items-center gap-4' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Number of stars:'),
|
|
||||||
Input({
|
|
||||||
type: 'number',
|
|
||||||
value: count,
|
|
||||||
class: 'input input-sm w-24',
|
|
||||||
oninput: (e) => count(parseInt(e.target.value) || 1)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: count,
|
|
||||||
onchange: (value) => rating(value)
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / ${count()}`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, customTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Product Review
|
|
||||||
const reviewTarget = document.querySelector('#demo-review');
|
|
||||||
if (reviewTarget && !reviewTarget.hasChildNodes()) {
|
|
||||||
const ReviewDemo = () => {
|
|
||||||
const quality = $(4);
|
|
||||||
const price = $(3);
|
|
||||||
const support = $(5);
|
|
||||||
|
|
||||||
const average = () => Math.round(((quality() + price() + support()) / 3) * 10) / 10;
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'text-lg font-bold' }, 'Product Review'),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm w-24' }, 'Quality:'),
|
|
||||||
Rating({
|
|
||||||
value: quality,
|
|
||||||
count: 5,
|
|
||||||
size: 'sm',
|
|
||||||
onchange: (v) => quality(v)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm w-24' }, 'Price:'),
|
|
||||||
Rating({
|
|
||||||
value: price,
|
|
||||||
count: 5,
|
|
||||||
size: 'sm',
|
|
||||||
onchange: (v) => price(v)
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm w-24' }, 'Support:'),
|
|
||||||
Rating({
|
|
||||||
value: support,
|
|
||||||
count: 5,
|
|
||||||
size: 'sm',
|
|
||||||
onchange: (v) => support(v)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'divider my-1' }),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'font-bold' }, 'Overall:'),
|
|
||||||
Div({ class: 'text-2xl font-bold text-primary' }, () => average())
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReviewDemo, reviewTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Mask Star'),
|
|
||||||
Rating({ value: $(3), count: 5, mask: 'mask-star' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Mask Star 2 (yellow)' ),
|
|
||||||
Rating({ value: $(4), count: 5, mask: 'mask-star-2', class: 'rating-warning' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Mask Heart'),
|
|
||||||
Rating({ value: $(5), count: 5, mask: 'mask-heart', class: 'rating-error' })
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Half Stars (read-only)'),
|
|
||||||
Rating({ value: $(3.5), count: 5, readonly: true })
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Interactive Feedback
|
|
||||||
const feedbackTarget = document.querySelector('#demo-feedback');
|
|
||||||
if (feedbackTarget && !feedbackTarget.hasChildNodes()) {
|
|
||||||
const FeedbackDemo = () => {
|
|
||||||
const rating = $(0);
|
|
||||||
const feedback = $(false);
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
1: 'Very disappointed 😞',
|
|
||||||
2: 'Could be better 😕',
|
|
||||||
3: 'Good 👍',
|
|
||||||
4: 'Very good 😊',
|
|
||||||
5: 'Excellent! 🎉'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'How was your experience?'),
|
|
||||||
Rating({
|
|
||||||
value: rating,
|
|
||||||
count: 5,
|
|
||||||
onchange: (value) => {
|
|
||||||
rating(value);
|
|
||||||
feedback(true);
|
|
||||||
if (value >= 4) {
|
|
||||||
Toast('Thank you for your positive feedback!', 'alert-success', 2000);
|
|
||||||
} else if (value <= 2) {
|
|
||||||
Toast('We appreciate your feedback and will improve!', 'alert-warning', 2000);
|
|
||||||
} else {
|
|
||||||
Toast('Thanks for your rating!', 'alert-info', 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
() => rating() > 0
|
|
||||||
? Div({ class: 'alert alert-soft text-center' }, [
|
|
||||||
messages[rating()] || `Rating: ${rating()} stars`
|
|
||||||
])
|
|
||||||
: null
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FeedbackDemo, feedbackTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initRatingExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initRatingExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
# 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>
|
|
||||||
@@ -1,519 +0,0 @@
|
|||||||
# Stack
|
|
||||||
|
|
||||||
Stack component for layering multiple elements on top of each other, creating depth and visual hierarchy.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Stack`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `Array<VNode> \| VNode` | `-` | Elements to stack (first is bottom, last is top) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Stack
|
|
||||||
|
|
||||||
<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 = () => {
|
|
||||||
return Stack({ class: 'w-40' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-lg p-4 shadow-lg' }, 'Layer 1'),
|
|
||||||
Div({ class: 'bg-secondary text-secondary-content rounded-lg p-4 shadow-lg' }, 'Layer 2'),
|
|
||||||
Div({ class: 'bg-accent text-accent-content rounded-lg p-4 shadow-lg' }, 'Layer 3')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Card Stack
|
|
||||||
|
|
||||||
<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-cards" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CardsDemo = () => {
|
|
||||||
return Stack({ class: 'w-64' }, [
|
|
||||||
Div({ class: 'card bg-base-100 shadow-xl border border-base-300' }, [
|
|
||||||
Div({ class: 'card-body p-4' }, [
|
|
||||||
Span({ class: 'text-sm opacity-70' }, 'Back Card'),
|
|
||||||
Span({ class: 'font-bold' }, 'Additional info')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'card bg-primary text-primary-content shadow-xl' }, [
|
|
||||||
Div({ class: 'card-body p-4' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Front Card'),
|
|
||||||
Span({ class: 'font-bold text-lg' }, 'Main Content')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CardsDemo, '#demo-cards');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Avatar Stack
|
|
||||||
|
|
||||||
<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-avatars" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AvatarsDemo = () => {
|
|
||||||
return Stack({ class: 'w-32' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-16' }, [
|
|
||||||
Span({}, 'JD')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-full w-16' }, [
|
|
||||||
Span({}, 'JS')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-secondary text-secondary-content rounded-full w-16' }, [
|
|
||||||
Span({}, 'BC')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AvatarsDemo, '#demo-avatars');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Image Stack
|
|
||||||
|
|
||||||
<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-images" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ImagesDemo = () => {
|
|
||||||
return Stack({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'w-full h-32 bg-gradient-to-r from-primary to-secondary rounded-lg shadow-lg' }, [
|
|
||||||
Div({ class: 'p-2 text-white text-sm' }, 'Background Image')
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-full h-32 bg-gradient-to-r from-secondary to-accent rounded-lg shadow-lg translate-x-2 translate-y-2' }, [
|
|
||||||
Div({ class: 'p-2 text-white text-sm' }, 'Middle Layer')
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-full h-32 bg-gradient-to-r from-accent to-primary rounded-lg shadow-lg translate-x-4 translate-y-4 flex items-center justify-center' }, [
|
|
||||||
Span({ class: 'text-white font-bold' }, 'Top Layer')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ImagesDemo, '#demo-images');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Photo Gallery Stack
|
|
||||||
|
|
||||||
<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-gallery" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const GalleryDemo = () => {
|
|
||||||
const photos = [
|
|
||||||
{ color: 'bg-primary', label: 'Photo 1' },
|
|
||||||
{ color: 'bg-secondary', label: 'Photo 2' },
|
|
||||||
{ color: 'bg-accent', label: 'Photo 3' },
|
|
||||||
{ color: 'bg-info', label: 'Photo 4' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Stack({ class: 'w-48 cursor-pointer hover:scale-105 transition-transform' }, [
|
|
||||||
...photos.map((photo, idx) =>
|
|
||||||
Div({
|
|
||||||
class: `${photo.color} rounded-lg shadow-lg transition-all`,
|
|
||||||
style: `transform: translate(${idx * 4}px, ${idx * 4}px); width: 100%; height: 100%;`
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 text-white font-bold' }, photo.label)
|
|
||||||
])
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(GalleryDemo, '#demo-gallery');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive Stack
|
|
||||||
|
|
||||||
<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-interactive" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const active = $(0);
|
|
||||||
const colors = ['primary', 'secondary', 'accent', 'info', 'success'];
|
|
||||||
const labels = ['Home', 'Profile', 'Settings', 'Messages', 'Notifications'];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6 items-center' }, [
|
|
||||||
Stack({ class: 'w-56' }, colors.map((color, idx) =>
|
|
||||||
Div({
|
|
||||||
class: `bg-${color} text-${color}-content rounded-lg p-4 shadow-lg transition-all cursor-pointer ${idx === active() ? 'scale-105 z-10' : ''}`,
|
|
||||||
style: `transform: translate(${idx * 8}px, ${idx * 8}px);`,
|
|
||||||
onclick: () => active(idx)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'font-bold' }, labels[idx]),
|
|
||||||
Div({ class: 'text-sm opacity-80' }, `Layer ${idx + 1}`)
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
Div({ class: 'mt-4 text-center' }, [
|
|
||||||
Span({ class: 'font-bold' }, () => `Active: ${labels[active()]}`),
|
|
||||||
Div({ class: 'flex gap-2 mt-2' }, colors.map((_, idx) =>
|
|
||||||
Button({
|
|
||||||
class: `btn btn-xs ${idx === active() ? 'btn-primary' : 'btn-ghost'}`,
|
|
||||||
onclick: () => active(idx)
|
|
||||||
}, `${idx + 1}`)
|
|
||||||
))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Notification Stack
|
|
||||||
|
|
||||||
<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-notifications" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const NotificationsDemo = () => {
|
|
||||||
const notifications = $([
|
|
||||||
{ id: 1, message: 'New message from John', type: 'info' },
|
|
||||||
{ id: 2, message: 'Your order has shipped', type: 'success' },
|
|
||||||
{ id: 3, message: 'Meeting in 10 minutes', type: 'warning' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const removeNotification = (id) => {
|
|
||||||
notifications(notifications().filter(n => n.id !== id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const typeClasses = {
|
|
||||||
info: 'bg-info text-info-content',
|
|
||||||
success: 'bg-success text-success-content',
|
|
||||||
warning: 'bg-warning text-warning-content',
|
|
||||||
error: 'bg-error text-error-content'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Stack({ class: 'w-80' }, notifications().map((notif, idx) =>
|
|
||||||
Div({
|
|
||||||
class: `${typeClasses[notif.type]} rounded-lg p-3 shadow-lg transition-all cursor-pointer`,
|
|
||||||
style: `transform: translate(${idx * 4}px, ${idx * 4}px);`,
|
|
||||||
onclick: () => removeNotification(notif.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm' }, notif.message),
|
|
||||||
Span({ class: 'text-xs opacity-70 cursor-pointer hover:opacity-100' }, '✕')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
notifications().length === 0
|
|
||||||
? Div({ class: 'alert alert-soft' }, 'No notifications')
|
|
||||||
: Button({
|
|
||||||
class: 'btn btn-sm btn-ghost mt-2',
|
|
||||||
onclick: () => notifications([])
|
|
||||||
}, 'Clear All')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(NotificationsDemo, '#demo-notifications');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Small Stack'),
|
|
||||||
Stack({ class: 'w-24' }, [
|
|
||||||
Div({ class: 'bg-primary rounded p-2 text-xs' }, '1'),
|
|
||||||
Div({ class: 'bg-secondary rounded p-2 text-xs' }, '2'),
|
|
||||||
Div({ class: 'bg-accent rounded p-2 text-xs' }, '3')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Medium Stack'),
|
|
||||||
Stack({ class: 'w-32' }, [
|
|
||||||
Div({ class: 'bg-primary rounded p-3' }, 'A'),
|
|
||||||
Div({ class: 'bg-secondary rounded p-3' }, 'B'),
|
|
||||||
Div({ class: 'bg-accent rounded p-3' }, 'C')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Large Stack'),
|
|
||||||
Stack({ class: 'w-40' }, [
|
|
||||||
Div({ class: 'bg-primary rounded p-4' }, 'X'),
|
|
||||||
Div({ class: 'bg-secondary rounded p-4' }, 'Y'),
|
|
||||||
Div({ class: 'bg-accent rounded p-4' }, 'Z')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initStackExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Stack
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Stack({ class: 'w-40' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-lg p-4 shadow-lg' }, 'Layer 1'),
|
|
||||||
Div({ class: 'bg-secondary text-secondary-content rounded-lg p-4 shadow-lg' }, 'Layer 2'),
|
|
||||||
Div({ class: 'bg-accent text-accent-content rounded-lg p-4 shadow-lg' }, 'Layer 3')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Card Stack
|
|
||||||
const cardsTarget = document.querySelector('#demo-cards');
|
|
||||||
if (cardsTarget && !cardsTarget.hasChildNodes()) {
|
|
||||||
const CardsDemo = () => {
|
|
||||||
return Stack({ class: 'w-64' }, [
|
|
||||||
Div({ class: 'card bg-base-100 shadow-xl border border-base-300' }, [
|
|
||||||
Div({ class: 'card-body p-4' }, [
|
|
||||||
Span({ class: 'text-sm opacity-70' }, 'Back Card'),
|
|
||||||
Span({ class: 'font-bold' }, 'Additional info')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'card bg-primary text-primary-content shadow-xl' }, [
|
|
||||||
Div({ class: 'card-body p-4' }, [
|
|
||||||
Span({ class: 'text-sm' }, 'Front Card'),
|
|
||||||
Span({ class: 'font-bold text-lg' }, 'Main Content')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CardsDemo, cardsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Avatar Stack
|
|
||||||
const avatarsTarget = document.querySelector('#demo-avatars');
|
|
||||||
if (avatarsTarget && !avatarsTarget.hasChildNodes()) {
|
|
||||||
const AvatarsDemo = () => {
|
|
||||||
return Stack({ class: 'w-32' }, [
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-neutral text-neutral-content rounded-full w-16' }, [
|
|
||||||
Span({}, 'JD')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-primary text-primary-content rounded-full w-16' }, [
|
|
||||||
Span({}, 'JS')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'avatar placeholder' }, [
|
|
||||||
Div({ class: 'bg-secondary text-secondary-content rounded-full w-16' }, [
|
|
||||||
Span({}, 'BC')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AvatarsDemo, avatarsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Image Stack
|
|
||||||
const imagesTarget = document.querySelector('#demo-images');
|
|
||||||
if (imagesTarget && !imagesTarget.hasChildNodes()) {
|
|
||||||
const ImagesDemo = () => {
|
|
||||||
return Stack({ class: 'w-48' }, [
|
|
||||||
Div({ class: 'w-full h-32 bg-gradient-to-r from-primary to-secondary rounded-lg shadow-lg' }, [
|
|
||||||
Div({ class: 'p-2 text-white text-sm' }, 'Background Image')
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-full h-32 bg-gradient-to-r from-secondary to-accent rounded-lg shadow-lg translate-x-2 translate-y-2' }, [
|
|
||||||
Div({ class: 'p-2 text-white text-sm' }, 'Middle Layer')
|
|
||||||
]),
|
|
||||||
Div({ class: 'w-full h-32 bg-gradient-to-r from-accent to-primary rounded-lg shadow-lg translate-x-4 translate-y-4 flex items-center justify-center' }, [
|
|
||||||
Span({ class: 'text-white font-bold' }, 'Top Layer')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ImagesDemo, imagesTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Photo Gallery Stack
|
|
||||||
const galleryTarget = document.querySelector('#demo-gallery');
|
|
||||||
if (galleryTarget && !galleryTarget.hasChildNodes()) {
|
|
||||||
const GalleryDemo = () => {
|
|
||||||
const photos = [
|
|
||||||
{ color: 'bg-primary', label: 'Photo 1' },
|
|
||||||
{ color: 'bg-secondary', label: 'Photo 2' },
|
|
||||||
{ color: 'bg-accent', label: 'Photo 3' },
|
|
||||||
{ color: 'bg-info', label: 'Photo 4' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Stack({ class: 'w-48 cursor-pointer hover:scale-105 transition-transform' }, [
|
|
||||||
...photos.map((photo, idx) =>
|
|
||||||
Div({
|
|
||||||
class: `${photo.color} rounded-lg shadow-lg transition-all`,
|
|
||||||
style: `transform: translate(${idx * 4}px, ${idx * 4}px); width: 100%; height: 100%;`
|
|
||||||
}, [
|
|
||||||
Div({ class: 'p-4 text-white font-bold' }, photo.label)
|
|
||||||
])
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(GalleryDemo, galleryTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Interactive Stack
|
|
||||||
const interactiveTarget = document.querySelector('#demo-interactive');
|
|
||||||
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const active = $(0);
|
|
||||||
const colors = ['primary', 'secondary', 'accent', 'info', 'success'];
|
|
||||||
const labels = ['Home', 'Profile', 'Settings', 'Messages', 'Notifications'];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6 items-center' }, [
|
|
||||||
Stack({ class: 'w-56' }, colors.map((color, idx) =>
|
|
||||||
Div({
|
|
||||||
class: `bg-${color} text-${color}-content rounded-lg p-4 shadow-lg transition-all cursor-pointer ${idx === active() ? 'scale-105 z-10' : ''}`,
|
|
||||||
style: `transform: translate(${idx * 8}px, ${idx * 8}px);`,
|
|
||||||
onclick: () => active(idx)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'font-bold' }, labels[idx]),
|
|
||||||
Div({ class: 'text-sm opacity-80' }, `Layer ${idx + 1}`)
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
Div({ class: 'mt-4 text-center' }, [
|
|
||||||
Span({ class: 'font-bold' }, () => `Active: ${labels[active()]}`),
|
|
||||||
Div({ class: 'flex gap-2 mt-2' }, colors.map((_, idx) =>
|
|
||||||
Button({
|
|
||||||
class: `btn btn-xs ${idx === active() ? 'btn-primary' : 'btn-ghost'}`,
|
|
||||||
onclick: () => active(idx)
|
|
||||||
}, `${idx + 1}`)
|
|
||||||
))
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, interactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Notification Stack
|
|
||||||
const notificationsTarget = document.querySelector('#demo-notifications');
|
|
||||||
if (notificationsTarget && !notificationsTarget.hasChildNodes()) {
|
|
||||||
const NotificationsDemo = () => {
|
|
||||||
const notifications = $([
|
|
||||||
{ id: 1, message: 'New message from John', type: 'info' },
|
|
||||||
{ id: 2, message: 'Your order has shipped', type: 'success' },
|
|
||||||
{ id: 3, message: 'Meeting in 10 minutes', type: 'warning' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const removeNotification = (id) => {
|
|
||||||
notifications(notifications().filter(n => n.id !== id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const typeClasses = {
|
|
||||||
info: 'bg-info text-info-content',
|
|
||||||
success: 'bg-success text-success-content',
|
|
||||||
warning: 'bg-warning text-warning-content',
|
|
||||||
error: 'bg-error text-error-content'
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Stack({ class: 'w-80' }, notifications().map((notif, idx) =>
|
|
||||||
Div({
|
|
||||||
class: `${typeClasses[notif.type]} rounded-lg p-3 shadow-lg transition-all cursor-pointer`,
|
|
||||||
style: `transform: translate(${idx * 4}px, ${idx * 4}px);`,
|
|
||||||
onclick: () => removeNotification(notif.id)
|
|
||||||
}, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'text-sm' }, notif.message),
|
|
||||||
Span({ class: 'text-xs opacity-70 cursor-pointer hover:opacity-100' }, '✕')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
)),
|
|
||||||
notifications().length === 0
|
|
||||||
? Div({ class: 'alert alert-soft' }, 'No notifications')
|
|
||||||
: Button({
|
|
||||||
class: 'btn btn-sm btn-ghost mt-2',
|
|
||||||
onclick: () => notifications([])
|
|
||||||
}, 'Clear All')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(NotificationsDemo, notificationsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Small Stack'),
|
|
||||||
Stack({ class: 'w-24' }, [
|
|
||||||
Div({ class: 'bg-primary rounded p-2 text-xs' }, '1'),
|
|
||||||
Div({ class: 'bg-secondary rounded p-2 text-xs' }, '2'),
|
|
||||||
Div({ class: 'bg-accent rounded p-2 text-xs' }, '3')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Medium Stack'),
|
|
||||||
Stack({ class: 'w-32' }, [
|
|
||||||
Div({ class: 'bg-primary rounded p-3' }, 'A'),
|
|
||||||
Div({ class: 'bg-secondary rounded p-3' }, 'B'),
|
|
||||||
Div({ class: 'bg-accent rounded p-3' }, 'C')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-sm mb-2' }, 'Large Stack'),
|
|
||||||
Stack({ class: 'w-40' }, [
|
|
||||||
Div({ class: 'bg-primary rounded p-4' }, 'X'),
|
|
||||||
Div({ class: 'bg-secondary rounded p-4' }, 'Y'),
|
|
||||||
Div({ class: 'bg-accent rounded p-4' }, 'Z')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initStackExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initStackExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,644 +0,0 @@
|
|||||||
# Stat
|
|
||||||
|
|
||||||
Statistic card component for displaying metrics, counts, and key performance indicators with optional icons and descriptions.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Stat`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `label` | `string \| VNode \| Signal` | `-` | Statistic label/title |
|
|
||||||
| `value` | `string \| number \| Signal` | `-` | Main statistic value |
|
|
||||||
| `desc` | `string \| VNode \| Signal` | `-` | Description or trend text |
|
|
||||||
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed in the figure area |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Stat
|
|
||||||
|
|
||||||
<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 grid grid-cols-1 md:grid-cols-3 gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Total Users',
|
|
||||||
value: '2,345',
|
|
||||||
desc: '↗︎ 120 new users this month'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Revenue',
|
|
||||||
value: '$45,678',
|
|
||||||
desc: '↘︎ 5% decrease from last month'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Conversion Rate',
|
|
||||||
value: '3.45%',
|
|
||||||
desc: '↗︎ 0.5% increase'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Icons
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 grid grid-cols-1 md:grid-cols-3 gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Active Users',
|
|
||||||
value: '1,234',
|
|
||||||
desc: 'Currently online',
|
|
||||||
icon: Icons.iconShow
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'New Orders',
|
|
||||||
value: '89',
|
|
||||||
desc: 'Today',
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Pending Tasks',
|
|
||||||
value: '23',
|
|
||||||
desc: 'Need attention',
|
|
||||||
icon: Icons.iconWarning
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Values
|
|
||||||
|
|
||||||
<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 count = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Counter',
|
|
||||||
value: () => count(),
|
|
||||||
desc: 'Click the button to increase',
|
|
||||||
icon: Icons.iconInfo
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Squared',
|
|
||||||
value: () => Math.pow(count(), 2),
|
|
||||||
desc: 'Square of counter',
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
}, 'Increment'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}, 'Reset')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Trend Indicators
|
|
||||||
|
|
||||||
<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-trends" class="bg-base-100 p-6 rounded-xl border border-base-300 grid grid-cols-1 md:grid-cols-3 gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const TrendsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Weekly Sales',
|
|
||||||
value: '$12,345',
|
|
||||||
desc: Div({ class: 'text-success' }, '↗︎ 15% increase'),
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Bounce Rate',
|
|
||||||
value: '42%',
|
|
||||||
desc: Div({ class: 'text-error' }, '↘︎ 3% from last week'),
|
|
||||||
icon: Icons.iconError
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Avg. Session',
|
|
||||||
value: '4m 32s',
|
|
||||||
desc: Div({ class: 'text-warning' }, '↗︎ 12 seconds'),
|
|
||||||
icon: Icons.iconWarning
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(TrendsDemo, '#demo-trends');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple Stats in Row
|
|
||||||
|
|
||||||
<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 = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-4 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Posts',
|
|
||||||
value: '1,234',
|
|
||||||
desc: 'Total content',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '📝')
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Comments',
|
|
||||||
value: '8,901',
|
|
||||||
desc: 'Engagement',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '💬')
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Likes',
|
|
||||||
value: '12,345',
|
|
||||||
desc: 'Reactions',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '❤️')
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Shares',
|
|
||||||
value: '456',
|
|
||||||
desc: 'Viral reach',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '🔄')
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MultipleDemo, '#demo-multiple');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dashboard 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-dashboard" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DashboardDemo = () => {
|
|
||||||
const stats = $({
|
|
||||||
users: 1245,
|
|
||||||
revenue: 89342,
|
|
||||||
orders: 342,
|
|
||||||
satisfaction: 94
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateStats = () => {
|
|
||||||
stats({
|
|
||||||
users: stats().users + Math.floor(Math.random() * 50),
|
|
||||||
revenue: stats().revenue + Math.floor(Math.random() * 1000),
|
|
||||||
orders: stats().orders + Math.floor(Math.random() * 20),
|
|
||||||
satisfaction: Math.min(100, stats().satisfaction + Math.floor(Math.random() * 5) - 2)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'grid grid-cols-1 md:grid-cols-4 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Total Users',
|
|
||||||
value: () => stats().users.toLocaleString(),
|
|
||||||
desc: 'Registered users',
|
|
||||||
icon: Icons.iconShow
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Revenue',
|
|
||||||
value: () => `$${stats().revenue.toLocaleString()}`,
|
|
||||||
desc: 'This month',
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Orders',
|
|
||||||
value: () => stats().orders.toLocaleString(),
|
|
||||||
desc: 'Completed',
|
|
||||||
icon: Icons.iconInfo
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Satisfaction',
|
|
||||||
value: () => `${stats().satisfaction}%`,
|
|
||||||
desc: stats().satisfaction > 90 ? 'Excellent!' : 'Good',
|
|
||||||
icon: Icons.iconWarning
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: updateStats
|
|
||||||
}, 'Refresh Data')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DashboardDemo, '#demo-dashboard');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 grid grid-cols-1 md:grid-cols-2 gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Primary Stat',
|
|
||||||
value: '1,234',
|
|
||||||
desc: 'With description',
|
|
||||||
icon: Icons.iconInfo,
|
|
||||||
class: 'bg-primary/10 text-primary'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Success Stat',
|
|
||||||
value: '89%',
|
|
||||||
desc: 'Success rate',
|
|
||||||
icon: Icons.iconSuccess,
|
|
||||||
class: 'bg-success/10 text-success'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Warning Stat',
|
|
||||||
value: '23',
|
|
||||||
desc: 'Pending items',
|
|
||||||
icon: Icons.iconWarning,
|
|
||||||
class: 'bg-warning/10 text-warning'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Error Stat',
|
|
||||||
value: '5',
|
|
||||||
desc: 'Failed attempts',
|
|
||||||
icon: Icons.iconError,
|
|
||||||
class: 'bg-error/10 text-error'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compact Stats
|
|
||||||
|
|
||||||
<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-compact" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CompactDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Views',
|
|
||||||
value: '12.3K',
|
|
||||||
class: 'stat-compact'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Likes',
|
|
||||||
value: '2,456',
|
|
||||||
class: 'stat-compact'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Comments',
|
|
||||||
value: '345',
|
|
||||||
class: 'stat-compact'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Shares',
|
|
||||||
value: '89',
|
|
||||||
class: 'stat-compact'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CompactDemo, '#demo-compact');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initStatExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Stat
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Total Users',
|
|
||||||
value: '2,345',
|
|
||||||
desc: '↗︎ 120 new users this month'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Revenue',
|
|
||||||
value: '$45,678',
|
|
||||||
desc: '↘︎ 5% decrease from last month'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Conversion Rate',
|
|
||||||
value: '3.45%',
|
|
||||||
desc: '↗︎ 0.5% increase'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Icons
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Active Users',
|
|
||||||
value: '1,234',
|
|
||||||
desc: 'Currently online',
|
|
||||||
icon: Icons.iconShow
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'New Orders',
|
|
||||||
value: '89',
|
|
||||||
desc: 'Today',
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Pending Tasks',
|
|
||||||
value: '23',
|
|
||||||
desc: 'Need attention',
|
|
||||||
icon: Icons.iconWarning
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Reactive Values
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Counter',
|
|
||||||
value: () => count(),
|
|
||||||
desc: 'Click the button to increase',
|
|
||||||
icon: Icons.iconInfo
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Squared',
|
|
||||||
value: () => Math.pow(count(), 2),
|
|
||||||
desc: 'Square of counter',
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
}, 'Increment'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost',
|
|
||||||
onclick: () => count(0)
|
|
||||||
}, 'Reset')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. With Trend Indicators
|
|
||||||
const trendsTarget = document.querySelector('#demo-trends');
|
|
||||||
if (trendsTarget && !trendsTarget.hasChildNodes()) {
|
|
||||||
const TrendsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Weekly Sales',
|
|
||||||
value: '$12,345',
|
|
||||||
desc: Div({ class: 'text-success' }, '↗︎ 15% increase'),
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Bounce Rate',
|
|
||||||
value: '42%',
|
|
||||||
desc: Div({ class: 'text-error' }, '↘︎ 3% from last week'),
|
|
||||||
icon: Icons.iconError
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Avg. Session',
|
|
||||||
value: '4m 32s',
|
|
||||||
desc: Div({ class: 'text-warning' }, '↗︎ 12 seconds'),
|
|
||||||
icon: Icons.iconWarning
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(TrendsDemo, trendsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Multiple Stats in Row
|
|
||||||
const multipleTarget = document.querySelector('#demo-multiple');
|
|
||||||
if (multipleTarget && !multipleTarget.hasChildNodes()) {
|
|
||||||
const MultipleDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-4 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Posts',
|
|
||||||
value: '1,234',
|
|
||||||
desc: 'Total content',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '📝')
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Comments',
|
|
||||||
value: '8,901',
|
|
||||||
desc: 'Engagement',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '💬')
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Likes',
|
|
||||||
value: '12,345',
|
|
||||||
desc: 'Reactions',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '❤️')
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Shares',
|
|
||||||
value: '456',
|
|
||||||
desc: 'Viral reach',
|
|
||||||
icon: Span({ class: 'text-2xl' }, '🔄')
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MultipleDemo, multipleTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Dashboard Example
|
|
||||||
const dashboardTarget = document.querySelector('#demo-dashboard');
|
|
||||||
if (dashboardTarget && !dashboardTarget.hasChildNodes()) {
|
|
||||||
const DashboardDemo = () => {
|
|
||||||
const stats = $({
|
|
||||||
users: 1245,
|
|
||||||
revenue: 89342,
|
|
||||||
orders: 342,
|
|
||||||
satisfaction: 94
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateStats = () => {
|
|
||||||
stats({
|
|
||||||
users: stats().users + Math.floor(Math.random() * 50),
|
|
||||||
revenue: stats().revenue + Math.floor(Math.random() * 1000),
|
|
||||||
orders: stats().orders + Math.floor(Math.random() * 20),
|
|
||||||
satisfaction: Math.min(100, stats().satisfaction + Math.floor(Math.random() * 5) - 2)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'grid grid-cols-1 md:grid-cols-4 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Total Users',
|
|
||||||
value: () => stats().users.toLocaleString(),
|
|
||||||
desc: 'Registered users',
|
|
||||||
icon: Icons.iconShow
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Revenue',
|
|
||||||
value: () => `$${stats().revenue.toLocaleString()}`,
|
|
||||||
desc: 'This month',
|
|
||||||
icon: Icons.iconSuccess
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Orders',
|
|
||||||
value: () => stats().orders.toLocaleString(),
|
|
||||||
desc: 'Completed',
|
|
||||||
icon: Icons.iconInfo
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Satisfaction',
|
|
||||||
value: () => `${stats().satisfaction}%`,
|
|
||||||
desc: stats().satisfaction > 90 ? 'Excellent!' : 'Good',
|
|
||||||
icon: Icons.iconWarning
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: updateStats
|
|
||||||
}, 'Refresh Data')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DashboardDemo, dashboardTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Primary Stat',
|
|
||||||
value: '1,234',
|
|
||||||
desc: 'With description',
|
|
||||||
icon: Icons.iconInfo,
|
|
||||||
class: 'bg-primary/10 text-primary'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Success Stat',
|
|
||||||
value: '89%',
|
|
||||||
desc: 'Success rate',
|
|
||||||
icon: Icons.iconSuccess,
|
|
||||||
class: 'bg-success/10 text-success'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Warning Stat',
|
|
||||||
value: '23',
|
|
||||||
desc: 'Pending items',
|
|
||||||
icon: Icons.iconWarning,
|
|
||||||
class: 'bg-warning/10 text-warning'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Error Stat',
|
|
||||||
value: '5',
|
|
||||||
desc: 'Failed attempts',
|
|
||||||
icon: Icons.iconError,
|
|
||||||
class: 'bg-error/10 text-error'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Compact Stats
|
|
||||||
const compactTarget = document.querySelector('#demo-compact');
|
|
||||||
if (compactTarget && !compactTarget.hasChildNodes()) {
|
|
||||||
const CompactDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4' }, [
|
|
||||||
Stat({
|
|
||||||
label: 'Views',
|
|
||||||
value: '12.3K',
|
|
||||||
class: 'stat-compact'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Likes',
|
|
||||||
value: '2,456',
|
|
||||||
class: 'stat-compact'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Comments',
|
|
||||||
value: '345',
|
|
||||||
class: 'stat-compact'
|
|
||||||
}),
|
|
||||||
Stat({
|
|
||||||
label: 'Shares',
|
|
||||||
value: '89',
|
|
||||||
class: 'stat-compact'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CompactDemo, compactTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initStatExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initStatExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,500 +0,0 @@
|
|||||||
# Swap
|
|
||||||
|
|
||||||
Toggle component that swaps between two states (on/off) with customizable icons or content.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Swap`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `value` | `boolean \| Signal<boolean>` | `false` | Swap state (true = on, false = off) |
|
|
||||||
| `on` | `string \| VNode` | `-` | Content to show when state is on |
|
|
||||||
| `off` | `string \| VNode` | `-` | Content to show when state is off |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `onclick` | `function` | `-` | Click event handler |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Swap
|
|
||||||
|
|
||||||
<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 isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: "🌟 ON",
|
|
||||||
off: "💫 OFF",
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Icon Swap
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: Icons.iconShow,
|
|
||||||
off: Icons.iconHide,
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Emoji Swap
|
|
||||||
|
|
||||||
<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-emoji" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const EmojiDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: "❤️",
|
|
||||||
off: "🖤",
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(EmojiDemo, '#demo-emoji');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Content Swap
|
|
||||||
|
|
||||||
<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-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: Div({ class: "badge badge-success gap-1" }, ["✅", " Active"]),
|
|
||||||
off: Div({ class: "badge badge-ghost gap-1" }, ["⭕", " Inactive"]),
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, '#demo-custom');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Reactive 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-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 isOn = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: Icons.iconShow,
|
|
||||||
off: Icons.iconHide,
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-center' }, () =>
|
|
||||||
isOn()
|
|
||||||
? Div({ class: 'alert alert-success' }, 'Content is visible')
|
|
||||||
: Div({ class: 'alert alert-soft' }, 'Content is hidden')
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Toggle Mode Swap
|
|
||||||
|
|
||||||
<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-mode" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ModeDemo = () => {
|
|
||||||
const darkMode = $(false);
|
|
||||||
const notifications = $(true);
|
|
||||||
const sound = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 w-full' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Dark mode'),
|
|
||||||
Swap({
|
|
||||||
value: darkMode,
|
|
||||||
on: "🌙",
|
|
||||||
off: "☀️",
|
|
||||||
onclick: () => darkMode(!darkMode())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Notifications'),
|
|
||||||
Swap({
|
|
||||||
value: notifications,
|
|
||||||
on: "🔔",
|
|
||||||
off: "🔕",
|
|
||||||
onclick: () => notifications(!notifications())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Sound effects'),
|
|
||||||
Swap({
|
|
||||||
value: sound,
|
|
||||||
on: "🔊",
|
|
||||||
off: "🔇",
|
|
||||||
onclick: () => sound(!sound())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'mt-2 p-3 rounded-lg', style: () => darkMode() ? 'background: #1f2937; color: white' : 'background: #f3f4f6' }, [
|
|
||||||
Div({ class: 'text-sm' }, () => `Mode: ${darkMode() ? 'Dark' : 'Light'} | Notifications: ${notifications() ? 'On' : 'Off'} | Sound: ${sound() ? 'On' : 'Off'}`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ModeDemo, '#demo-mode');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Volume'),
|
|
||||||
Swap({
|
|
||||||
value: $(false),
|
|
||||||
on: "🔊",
|
|
||||||
off: "🔇"
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Like'),
|
|
||||||
Swap({
|
|
||||||
value: $(true),
|
|
||||||
on: "❤️",
|
|
||||||
off: "🤍"
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Star'),
|
|
||||||
Swap({
|
|
||||||
value: $(false),
|
|
||||||
on: "⭐",
|
|
||||||
off: "☆"
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Check'),
|
|
||||||
Swap({
|
|
||||||
value: $(true),
|
|
||||||
on: Icons.iconSuccess,
|
|
||||||
off: Icons.iconError
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Simple Todo 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-todo" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const TodoDemo = () => {
|
|
||||||
const todos = [
|
|
||||||
{ id: 1, text: 'Complete documentation', completed: $(true) },
|
|
||||||
{ id: 2, text: 'Review pull requests', completed: $(false) },
|
|
||||||
{ id: 3, text: 'Deploy to production', completed: $(false) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Todo list'),
|
|
||||||
...todos.map(todo =>
|
|
||||||
Div({ class: 'flex items-center justify-between p-2 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({ class: todo.completed() ? 'line-through opacity-50' : '' }, todo.text),
|
|
||||||
Swap({
|
|
||||||
value: todo.completed,
|
|
||||||
on: Icons.iconSuccess,
|
|
||||||
off: Icons.iconClose,
|
|
||||||
onclick: () => todo.completed(!todo.completed())
|
|
||||||
})
|
|
||||||
])
|
|
||||||
),
|
|
||||||
Div({ class: 'mt-2 text-sm opacity-70' }, () => {
|
|
||||||
const completed = todos.filter(t => t.completed()).length;
|
|
||||||
return `${completed} of ${todos.length} tasks completed`;
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(TodoDemo, '#demo-todo');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initSwapExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Swap
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: "🌟 ON",
|
|
||||||
off: "💫 OFF",
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Icon Swap
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: Icons.iconShow,
|
|
||||||
off: Icons.iconHide,
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Emoji Swap
|
|
||||||
const emojiTarget = document.querySelector('#demo-emoji');
|
|
||||||
if (emojiTarget && !emojiTarget.hasChildNodes()) {
|
|
||||||
const EmojiDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: "❤️",
|
|
||||||
off: "🖤",
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(EmojiDemo, emojiTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Custom Content Swap
|
|
||||||
const customTarget = document.querySelector('#demo-custom');
|
|
||||||
if (customTarget && !customTarget.hasChildNodes()) {
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: Div({ class: "badge badge-success gap-1" }, ["✅", " Active"]),
|
|
||||||
off: Div({ class: "badge badge-ghost gap-1" }, ["⭕", " Inactive"]),
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, customTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Reactive State
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const isOn = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Swap({
|
|
||||||
value: isOn,
|
|
||||||
on: Icons.iconShow,
|
|
||||||
off: Icons.iconHide,
|
|
||||||
onclick: () => isOn(!isOn())
|
|
||||||
}),
|
|
||||||
Div({ class: 'text-center' }, () =>
|
|
||||||
isOn()
|
|
||||||
? Div({ class: 'alert alert-success' }, 'Content is visible')
|
|
||||||
: Div({ class: 'alert alert-soft' }, 'Content is hidden')
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Toggle Mode Swap
|
|
||||||
const modeTarget = document.querySelector('#demo-mode');
|
|
||||||
if (modeTarget && !modeTarget.hasChildNodes()) {
|
|
||||||
const ModeDemo = () => {
|
|
||||||
const darkMode = $(false);
|
|
||||||
const notifications = $(true);
|
|
||||||
const sound = $(false);
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 w-full' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Dark mode'),
|
|
||||||
Swap({
|
|
||||||
value: darkMode,
|
|
||||||
on: "🌙",
|
|
||||||
off: "☀️",
|
|
||||||
onclick: () => darkMode(!darkMode())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Notifications'),
|
|
||||||
Swap({
|
|
||||||
value: notifications,
|
|
||||||
on: "🔔",
|
|
||||||
off: "🔕",
|
|
||||||
onclick: () => notifications(!notifications())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({}, 'Sound effects'),
|
|
||||||
Swap({
|
|
||||||
value: sound,
|
|
||||||
on: "🔊",
|
|
||||||
off: "🔇",
|
|
||||||
onclick: () => sound(!sound())
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'mt-2 p-3 rounded-lg', style: () => darkMode() ? 'background: #1f2937; color: white' : 'background: #f3f4f6' }, [
|
|
||||||
Div({ class: 'text-sm' }, () => `Mode: ${darkMode() ? 'Dark' : 'Light'} | Notifications: ${notifications() ? 'On' : 'Off'} | Sound: ${sound() ? 'On' : 'Off'}`)
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ModeDemo, modeTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Volume'),
|
|
||||||
Swap({
|
|
||||||
value: $(false),
|
|
||||||
on: "🔊",
|
|
||||||
off: "🔇"
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Like'),
|
|
||||||
Swap({
|
|
||||||
value: $(true),
|
|
||||||
on: "❤️",
|
|
||||||
off: "🤍"
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Star'),
|
|
||||||
Swap({
|
|
||||||
value: $(false),
|
|
||||||
on: "⭐",
|
|
||||||
off: "☆"
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-center' }, [
|
|
||||||
Div({ class: 'text-xs mb-2' }, 'Check'),
|
|
||||||
Swap({
|
|
||||||
value: $(true),
|
|
||||||
on: Icons.iconSuccess,
|
|
||||||
off: Icons.iconError
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Simple Todo Toggle
|
|
||||||
const todoTarget = document.querySelector('#demo-todo');
|
|
||||||
if (todoTarget && !todoTarget.hasChildNodes()) {
|
|
||||||
const TodoDemo = () => {
|
|
||||||
const todos = [
|
|
||||||
{ id: 1, text: 'Complete documentation', completed: $(true) },
|
|
||||||
{ id: 2, text: 'Review pull requests', completed: $(false) },
|
|
||||||
{ id: 3, text: 'Deploy to production', completed: $(false) }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-3' }, [
|
|
||||||
Div({ class: 'text-sm font-bold mb-2' }, 'Todo list'),
|
|
||||||
...todos.map(todo =>
|
|
||||||
Div({ class: 'flex items-center justify-between p-2 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({ class: todo.completed() ? 'line-through opacity-50' : '' }, todo.text),
|
|
||||||
Swap({
|
|
||||||
value: todo.completed,
|
|
||||||
on: Icons.iconSuccess,
|
|
||||||
off: Icons.iconClose,
|
|
||||||
onclick: () => todo.completed(!todo.completed())
|
|
||||||
})
|
|
||||||
])
|
|
||||||
),
|
|
||||||
Div({ class: 'mt-2 text-sm opacity-70' }, () => {
|
|
||||||
const completed = todos.filter(t => t.completed()).length;
|
|
||||||
return `${completed} of ${todos.length} tasks completed`;
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(TodoDemo, todoTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initSwapExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initSwapExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,716 +0,0 @@
|
|||||||
# Table
|
|
||||||
|
|
||||||
Data table component with sorting, pagination, zebra stripes, pin rows, and custom cell rendering.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Table`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :--------------- | :----------------------------------------------- |
|
|
||||||
| `items` | `Array \| Signal<Array>` | `[]` | Data array to display |
|
|
||||||
| `columns` | `Array<{label: string, key?: string, render?: function, class?: string, footer?: string}>` | `[]` | Column definitions |
|
|
||||||
| `keyFn` | `function` | `(item, idx) => idx` | Unique key function for rows |
|
|
||||||
| `zebra` | `boolean \| Signal<boolean>` | `false` | Enable zebra striping |
|
|
||||||
| `pinRows` | `boolean \| Signal<boolean>` | `false` | Pin header rows on scroll |
|
|
||||||
| `empty` | `string \| VNode` | `'No data'` | Content to show when no data |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Table
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const users = [
|
|
||||||
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
|
|
||||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
|
|
||||||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: users,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Email', key: 'email' },
|
|
||||||
{ label: 'Role', key: 'role' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Zebra Stripes
|
|
||||||
|
|
||||||
<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-zebra" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ZebraDemo = () => {
|
|
||||||
const products = [
|
|
||||||
{ id: 1, name: 'Laptop', price: '$999', stock: 15 },
|
|
||||||
{ id: 2, name: 'Mouse', price: '$29', stock: 42 },
|
|
||||||
{ id: 3, name: 'Keyboard', price: '$79', stock: 28 },
|
|
||||||
{ id: 4, name: 'Monitor', price: '$299', stock: 12 }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: products,
|
|
||||||
columns: [
|
|
||||||
{ label: 'Product', key: 'name' },
|
|
||||||
{ label: 'Price', key: 'price' },
|
|
||||||
{ label: 'Stock', key: 'stock' }
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ZebraDemo, '#demo-zebra');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Custom Cell Rendering
|
|
||||||
|
|
||||||
<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-custom" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const orders = [
|
|
||||||
{ id: 101, customer: 'Alice', amount: 250, status: 'completed' },
|
|
||||||
{ id: 102, customer: 'Bob', amount: 89, status: 'pending' },
|
|
||||||
{ id: 103, customer: 'Charlie', amount: 450, status: 'shipped' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: orders,
|
|
||||||
columns: [
|
|
||||||
{ label: 'Order ID', key: 'id' },
|
|
||||||
{ label: 'Customer', key: 'customer' },
|
|
||||||
{
|
|
||||||
label: 'Amount',
|
|
||||||
key: 'amount',
|
|
||||||
render: (item) => `$${item.amount}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
key: 'status',
|
|
||||||
render: (item) => {
|
|
||||||
const statusClass = {
|
|
||||||
completed: 'badge badge-success',
|
|
||||||
pending: 'badge badge-warning',
|
|
||||||
shipped: 'badge badge-info'
|
|
||||||
};
|
|
||||||
return Span({ class: statusClass[item.status] }, item.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, '#demo-custom');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Footers
|
|
||||||
|
|
||||||
<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-footer" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const FooterDemo = () => {
|
|
||||||
const sales = [
|
|
||||||
{ month: 'January', revenue: 12500, expenses: 8900 },
|
|
||||||
{ month: 'February', revenue: 14200, expenses: 9200 },
|
|
||||||
{ month: 'March', revenue: 16800, expenses: 10100 }
|
|
||||||
];
|
|
||||||
|
|
||||||
const totalRevenue = sales.reduce((sum, item) => sum + item.revenue, 0);
|
|
||||||
const totalExpenses = sales.reduce((sum, item) => sum + item.expenses, 0);
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: sales,
|
|
||||||
columns: [
|
|
||||||
{ label: 'Month', key: 'month' },
|
|
||||||
{
|
|
||||||
label: 'Revenue',
|
|
||||||
key: 'revenue',
|
|
||||||
render: (item) => `$${item.revenue.toLocaleString()}`,
|
|
||||||
footer: `Total: $${totalRevenue.toLocaleString()}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Expenses',
|
|
||||||
key: 'expenses',
|
|
||||||
render: (item) => `$${item.expenses.toLocaleString()}`,
|
|
||||||
footer: `Total: $${totalExpenses.toLocaleString()}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Profit',
|
|
||||||
render: (item) => `$${(item.revenue - item.expenses).toLocaleString()}`,
|
|
||||||
footer: `$${(totalRevenue - totalExpenses).toLocaleString()}`
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(FooterDemo, '#demo-footer');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Empty 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-empty" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const EmptyDemo = () => {
|
|
||||||
const emptyList = [];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: emptyList,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' }
|
|
||||||
],
|
|
||||||
empty: Div({ class: 'flex flex-col items-center gap-2' }, [
|
|
||||||
Icons.iconInfo,
|
|
||||||
Span({}, 'No records found')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(EmptyDemo, '#demo-empty');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Data
|
|
||||||
|
|
||||||
<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 filter = $('all');
|
|
||||||
const tasks = $([
|
|
||||||
{ id: 1, title: 'Complete documentation', completed: true },
|
|
||||||
{ id: 2, title: 'Review pull requests', completed: false },
|
|
||||||
{ id: 3, title: 'Deploy to production', completed: false },
|
|
||||||
{ id: 4, title: 'Update dependencies', completed: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const filteredTasks = () => {
|
|
||||||
if (filter() === 'completed') {
|
|
||||||
return tasks().filter(t => t.completed);
|
|
||||||
} else if (filter() === 'pending') {
|
|
||||||
return tasks().filter(t => !t.completed);
|
|
||||||
}
|
|
||||||
return tasks();
|
|
||||||
};
|
|
||||||
|
|
||||||
const addTask = () => {
|
|
||||||
const newId = Math.max(...tasks().map(t => t.id), 0) + 1;
|
|
||||||
tasks([...tasks(), { id: newId, title: `Task ${newId}`, completed: false }]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => filter('all')
|
|
||||||
}, 'All'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => filter('completed')
|
|
||||||
}, 'Completed'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => filter('pending')
|
|
||||||
}, 'Pending'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-primary',
|
|
||||||
onclick: addTask
|
|
||||||
}, 'Add Task')
|
|
||||||
]),
|
|
||||||
Table({
|
|
||||||
items: filteredTasks,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Title', key: 'title' },
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
render: (item) => item.completed
|
|
||||||
? Span({ class: 'badge badge-success' }, '✓ Done')
|
|
||||||
: Span({ class: 'badge badge-warning' }, '○ Pending')
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Actions
|
|
||||||
|
|
||||||
<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-actions" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const users = $([
|
|
||||||
{ id: 1, name: 'John Doe', email: 'john@example.com', active: true },
|
|
||||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false },
|
|
||||||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', active: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const deleteUser = (id) => {
|
|
||||||
users(users().filter(u => u.id !== id));
|
|
||||||
Toast('User deleted', 'alert-info', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleActive = (id) => {
|
|
||||||
users(users().map(u =>
|
|
||||||
u.id === id ? { ...u, active: !u.active } : u
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: users,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Email', key: 'email' },
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
render: (item) => item.active
|
|
||||||
? Span({ class: 'badge badge-success' }, 'Active')
|
|
||||||
: Span({ class: 'badge badge-ghost' }, 'Inactive')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Actions',
|
|
||||||
render: (item) => Div({ class: 'flex gap-1' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-ghost',
|
|
||||||
onclick: () => toggleActive(item.id)
|
|
||||||
}, item.active ? 'Deactivate' : 'Activate'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-error',
|
|
||||||
onclick: () => deleteUser(item.id)
|
|
||||||
}, 'Delete')
|
|
||||||
])
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, '#demo-actions');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-6"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const data = [
|
|
||||||
{ id: 1, name: 'Item 1', value: 100 },
|
|
||||||
{ id: 2, name: 'Item 2', value: 200 },
|
|
||||||
{ id: 3, name: 'Item 3', value: 300 }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Table'),
|
|
||||||
Table({
|
|
||||||
items: data,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Value', key: 'value' }
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Zebra Stripes'),
|
|
||||||
Table({
|
|
||||||
items: data,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Value', key: 'value' }
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Compact Table'),
|
|
||||||
Table({
|
|
||||||
items: data,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Value', key: 'value' }
|
|
||||||
],
|
|
||||||
class: 'table-compact'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initTableExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Table
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const users = [
|
|
||||||
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
|
|
||||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
|
|
||||||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: users,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Email', key: 'email' },
|
|
||||||
{ label: 'Role', key: 'role' }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Zebra Stripes
|
|
||||||
const zebraTarget = document.querySelector('#demo-zebra');
|
|
||||||
if (zebraTarget && !zebraTarget.hasChildNodes()) {
|
|
||||||
const ZebraDemo = () => {
|
|
||||||
const products = [
|
|
||||||
{ id: 1, name: 'Laptop', price: '$999', stock: 15 },
|
|
||||||
{ id: 2, name: 'Mouse', price: '$29', stock: 42 },
|
|
||||||
{ id: 3, name: 'Keyboard', price: '$79', stock: 28 },
|
|
||||||
{ id: 4, name: 'Monitor', price: '$299', stock: 12 }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: products,
|
|
||||||
columns: [
|
|
||||||
{ label: 'Product', key: 'name' },
|
|
||||||
{ label: 'Price', key: 'price' },
|
|
||||||
{ label: 'Stock', key: 'stock' }
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ZebraDemo, zebraTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. With Custom Cell Rendering
|
|
||||||
const customTarget = document.querySelector('#demo-custom');
|
|
||||||
if (customTarget && !customTarget.hasChildNodes()) {
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const orders = [
|
|
||||||
{ id: 101, customer: 'Alice', amount: 250, status: 'completed' },
|
|
||||||
{ id: 102, customer: 'Bob', amount: 89, status: 'pending' },
|
|
||||||
{ id: 103, customer: 'Charlie', amount: 450, status: 'shipped' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: orders,
|
|
||||||
columns: [
|
|
||||||
{ label: 'Order ID', key: 'id' },
|
|
||||||
{ label: 'Customer', key: 'customer' },
|
|
||||||
{
|
|
||||||
label: 'Amount',
|
|
||||||
key: 'amount',
|
|
||||||
render: (item) => `$${item.amount}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
key: 'status',
|
|
||||||
render: (item) => {
|
|
||||||
const statusClass = {
|
|
||||||
completed: 'badge badge-success',
|
|
||||||
pending: 'badge badge-warning',
|
|
||||||
shipped: 'badge badge-info'
|
|
||||||
};
|
|
||||||
return Span({ class: statusClass[item.status] }, item.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, customTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. With Footers
|
|
||||||
const footerTarget = document.querySelector('#demo-footer');
|
|
||||||
if (footerTarget && !footerTarget.hasChildNodes()) {
|
|
||||||
const FooterDemo = () => {
|
|
||||||
const sales = [
|
|
||||||
{ month: 'January', revenue: 12500, expenses: 8900 },
|
|
||||||
{ month: 'February', revenue: 14200, expenses: 9200 },
|
|
||||||
{ month: 'March', revenue: 16800, expenses: 10100 }
|
|
||||||
];
|
|
||||||
|
|
||||||
const totalRevenue = sales.reduce((sum, item) => sum + item.revenue, 0);
|
|
||||||
const totalExpenses = sales.reduce((sum, item) => sum + item.expenses, 0);
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: sales,
|
|
||||||
columns: [
|
|
||||||
{ label: 'Month', key: 'month' },
|
|
||||||
{
|
|
||||||
label: 'Revenue',
|
|
||||||
key: 'revenue',
|
|
||||||
render: (item) => `$${item.revenue.toLocaleString()}`,
|
|
||||||
footer: `Total: $${totalRevenue.toLocaleString()}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Expenses',
|
|
||||||
key: 'expenses',
|
|
||||||
render: (item) => `$${item.expenses.toLocaleString()}`,
|
|
||||||
footer: `Total: $${totalExpenses.toLocaleString()}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Profit',
|
|
||||||
render: (item) => `$${(item.revenue - item.expenses).toLocaleString()}`,
|
|
||||||
footer: `$${(totalRevenue - totalExpenses).toLocaleString()}`
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(FooterDemo, footerTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Empty State
|
|
||||||
const emptyTarget = document.querySelector('#demo-empty');
|
|
||||||
if (emptyTarget && !emptyTarget.hasChildNodes()) {
|
|
||||||
const EmptyDemo = () => {
|
|
||||||
const emptyList = [];
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: emptyList,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' }
|
|
||||||
],
|
|
||||||
empty: Div({ class: 'flex flex-col items-center gap-2' }, [
|
|
||||||
Icons.iconInfo,
|
|
||||||
Span({}, 'No records found')
|
|
||||||
])
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(EmptyDemo, emptyTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Reactive Data
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const filter = $('all');
|
|
||||||
const tasks = $([
|
|
||||||
{ id: 1, title: 'Complete documentation', completed: true },
|
|
||||||
{ id: 2, title: 'Review pull requests', completed: false },
|
|
||||||
{ id: 3, title: 'Deploy to production', completed: false },
|
|
||||||
{ id: 4, title: 'Update dependencies', completed: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const filteredTasks = () => {
|
|
||||||
if (filter() === 'completed') {
|
|
||||||
return tasks().filter(t => t.completed);
|
|
||||||
} else if (filter() === 'pending') {
|
|
||||||
return tasks().filter(t => !t.completed);
|
|
||||||
}
|
|
||||||
return tasks();
|
|
||||||
};
|
|
||||||
|
|
||||||
const addTask = () => {
|
|
||||||
const newId = Math.max(...tasks().map(t => t.id), 0) + 1;
|
|
||||||
tasks([...tasks(), { id: newId, title: `Task ${newId}`, completed: false }]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex gap-2' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => filter('all')
|
|
||||||
}, 'All'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => filter('completed')
|
|
||||||
}, 'Completed'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => filter('pending')
|
|
||||||
}, 'Pending'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-primary',
|
|
||||||
onclick: addTask
|
|
||||||
}, 'Add Task')
|
|
||||||
]),
|
|
||||||
Table({
|
|
||||||
items: filteredTasks,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Title', key: 'title' },
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
render: (item) => item.completed
|
|
||||||
? Span({ class: 'badge badge-success' }, '✓ Done')
|
|
||||||
: Span({ class: 'badge badge-warning' }, '○ Pending')
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. With Actions
|
|
||||||
const actionsTarget = document.querySelector('#demo-actions');
|
|
||||||
if (actionsTarget && !actionsTarget.hasChildNodes()) {
|
|
||||||
const ActionsDemo = () => {
|
|
||||||
const users = $([
|
|
||||||
{ id: 1, name: 'John Doe', email: 'john@example.com', active: true },
|
|
||||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false },
|
|
||||||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', active: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const deleteUser = (id) => {
|
|
||||||
users(users().filter(u => u.id !== id));
|
|
||||||
Toast('User deleted', 'alert-info', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleActive = (id) => {
|
|
||||||
users(users().map(u =>
|
|
||||||
u.id === id ? { ...u, active: !u.active } : u
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
return Table({
|
|
||||||
items: users,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Email', key: 'email' },
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
render: (item) => item.active
|
|
||||||
? Span({ class: 'badge badge-success' }, 'Active')
|
|
||||||
: Span({ class: 'badge badge-ghost' }, 'Inactive')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Actions',
|
|
||||||
render: (item) => Div({ class: 'flex gap-1' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-ghost',
|
|
||||||
onclick: () => toggleActive(item.id)
|
|
||||||
}, item.active ? 'Deactivate' : 'Activate'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs btn-error',
|
|
||||||
onclick: () => deleteUser(item.id)
|
|
||||||
}, 'Delete')
|
|
||||||
])
|
|
||||||
}
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ActionsDemo, actionsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const data = [
|
|
||||||
{ id: 1, name: 'Item 1', value: 100 },
|
|
||||||
{ id: 2, name: 'Item 2', value: 200 },
|
|
||||||
{ id: 3, name: 'Item 3', value: 300 }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Table'),
|
|
||||||
Table({
|
|
||||||
items: data,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Value', key: 'value' }
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Zebra Stripes'),
|
|
||||||
Table({
|
|
||||||
items: data,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Value', key: 'value' }
|
|
||||||
],
|
|
||||||
zebra: true
|
|
||||||
}),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Compact Table'),
|
|
||||||
Table({
|
|
||||||
items: data,
|
|
||||||
columns: [
|
|
||||||
{ label: 'ID', key: 'id' },
|
|
||||||
{ label: 'Name', key: 'name' },
|
|
||||||
{ label: 'Value', key: 'value' }
|
|
||||||
],
|
|
||||||
class: 'table-compact'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initTableExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initTableExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,677 +0,0 @@
|
|||||||
# Tabs
|
|
||||||
|
|
||||||
Tabs component for organizing content into separate panels with tab navigation.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Tabs`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `items` | `Array<TabItem> \| Signal<Array>` | `[]` | Array of tab items |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
### TabItem Structure
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| :---------- | :--------------------------- | :----------------------------------------------- |
|
|
||||||
| `label` | `string \| VNode` | Tab button label |
|
|
||||||
| `content` | `VNode \| function` | Content to display when tab is active |
|
|
||||||
| `active` | `boolean \| Signal<boolean>` | Whether this tab is active (only one per group) |
|
|
||||||
| `disabled` | `boolean` | Whether tab is disabled |
|
|
||||||
| `tip` | `string` | Tooltip text for the tab |
|
|
||||||
| `onclick` | `function` | Click handler (optional, overrides default) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Tabs
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const activeTab = $('tab1');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Tab 1',
|
|
||||||
active: () => activeTab() === 'tab1',
|
|
||||||
onclick: () => activeTab('tab1'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Content for Tab 1')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Tab 2',
|
|
||||||
active: () => activeTab() === 'tab2',
|
|
||||||
onclick: () => activeTab('tab2'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Content for Tab 2')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Tab 3',
|
|
||||||
active: () => activeTab() === 'tab3',
|
|
||||||
onclick: () => activeTab('tab3'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Content for Tab 3')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Icons
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const activeTab = $('home');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, ['🏠', 'Home']),
|
|
||||||
active: () => activeTab() === 'home',
|
|
||||||
onclick: () => activeTab('home'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Welcome to the Home tab!')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, ['⭐', 'Favorites']),
|
|
||||||
active: () => activeTab() === 'favorites',
|
|
||||||
onclick: () => activeTab('favorites'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Your favorite items appear here.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, ['⚙️', 'Settings']),
|
|
||||||
active: () => activeTab() === 'settings',
|
|
||||||
onclick: () => activeTab('settings'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Configure your preferences.')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Tooltips
|
|
||||||
|
|
||||||
<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-tooltips" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const TooltipsDemo = () => {
|
|
||||||
const activeTab = $('profile');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Profile',
|
|
||||||
tip: 'View your profile information',
|
|
||||||
active: () => activeTab() === 'profile',
|
|
||||||
onclick: () => activeTab('profile'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Profile information here.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Settings',
|
|
||||||
tip: 'Adjust your preferences',
|
|
||||||
active: () => activeTab() === 'settings',
|
|
||||||
onclick: () => activeTab('settings'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Settings configuration.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Notifications',
|
|
||||||
tip: 'Manage notifications',
|
|
||||||
active: () => activeTab() === 'notifications',
|
|
||||||
onclick: () => activeTab('notifications'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Notification settings.')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(TooltipsDemo, '#demo-tooltips');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Disabled Tab
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DisabledDemo = () => {
|
|
||||||
const activeTab = $('basic');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Basic',
|
|
||||||
active: () => activeTab() === 'basic',
|
|
||||||
onclick: () => activeTab('basic'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Basic features available.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Premium',
|
|
||||||
disabled: true,
|
|
||||||
tip: 'Upgrade to access',
|
|
||||||
content: Div({ class: 'p-4' }, 'Premium content (locked)')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Pro',
|
|
||||||
disabled: true,
|
|
||||||
tip: 'Coming soon',
|
|
||||||
content: Div({ class: 'p-4' }, 'Pro features (coming soon)')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(DisabledDemo, '#demo-disabled');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Content
|
|
||||||
|
|
||||||
<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 activeTab = $('counter');
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Counter',
|
|
||||||
active: () => activeTab() === 'counter',
|
|
||||||
onclick: () => activeTab('counter'),
|
|
||||||
content: Div({ class: 'p-4 text-center' }, [
|
|
||||||
Div({ class: 'text-4xl font-bold mb-4' }, () => count()),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
}, 'Increment')
|
|
||||||
])
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Timer',
|
|
||||||
active: () => activeTab() === 'timer',
|
|
||||||
onclick: () => activeTab('timer'),
|
|
||||||
content: Div({ class: 'p-4' }, () => `Current time: ${new Date().toLocaleTimeString()}`)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
active: () => activeTab() === 'status',
|
|
||||||
onclick: () => activeTab('status'),
|
|
||||||
content: Div({ class: 'p-4' }, () => `Counter value: ${count()}, Last updated: ${new Date().toLocaleTimeString()}`)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Form Tabs
|
|
||||||
|
|
||||||
<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 FormTabs = () => {
|
|
||||||
const activeTab = $('personal');
|
|
||||||
const formData = $({
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
address: '',
|
|
||||||
city: '',
|
|
||||||
notifications: true,
|
|
||||||
newsletter: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateField = (field, value) => {
|
|
||||||
formData({ ...formData(), [field]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
Toast('Form submitted!', 'alert-success', 2000);
|
|
||||||
console.log(formData());
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Personal Info',
|
|
||||||
active: () => activeTab() === 'personal',
|
|
||||||
onclick: () => activeTab('personal'),
|
|
||||||
content: Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Name',
|
|
||||||
value: () => formData().name,
|
|
||||||
placeholder: 'Enter your name',
|
|
||||||
oninput: (e) => updateField('name', e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: () => formData().email,
|
|
||||||
placeholder: 'email@example.com',
|
|
||||||
oninput: (e) => updateField('email', e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Address',
|
|
||||||
active: () => activeTab() === 'address',
|
|
||||||
onclick: () => activeTab('address'),
|
|
||||||
content: Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Address',
|
|
||||||
value: () => formData().address,
|
|
||||||
placeholder: 'Street address',
|
|
||||||
oninput: (e) => updateField('address', e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'City',
|
|
||||||
value: () => formData().city,
|
|
||||||
placeholder: 'City',
|
|
||||||
oninput: (e) => updateField('city', e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Preferences',
|
|
||||||
active: () => activeTab() === 'prefs',
|
|
||||||
onclick: () => activeTab('prefs'),
|
|
||||||
content: Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Checkbox({
|
|
||||||
label: 'Email notifications',
|
|
||||||
value: () => formData().notifications,
|
|
||||||
onclick: () => updateField('notifications', !formData().notifications)
|
|
||||||
}),
|
|
||||||
Checkbox({
|
|
||||||
label: 'Newsletter subscription',
|
|
||||||
value: () => formData().newsletter,
|
|
||||||
onclick: () => updateField('newsletter', !formData().newsletter)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Div({ class: 'flex justify-end mt-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Submit')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormTabs, '#demo-form');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-6"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const items = [
|
|
||||||
{ label: 'Tab 1', content: 'Content 1' },
|
|
||||||
{ label: 'Tab 2', content: 'Content 2' },
|
|
||||||
{ label: 'Tab 3', content: 'Content 3' }
|
|
||||||
].map((tab, idx) => ({
|
|
||||||
...tab,
|
|
||||||
active: () => idx === 0,
|
|
||||||
content: Div({ class: 'p-4' }, tab.content)
|
|
||||||
}));
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Tabs'),
|
|
||||||
Tabs({ items }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Boxed Tabs'),
|
|
||||||
Tabs({ items, class: 'tabs-box' }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Lifted Tabs'),
|
|
||||||
Tabs({ items, class: 'tabs-lifted' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initTabsExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Tabs
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const activeTab = $('tab1');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Tab 1',
|
|
||||||
active: () => activeTab() === 'tab1',
|
|
||||||
onclick: () => activeTab('tab1'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Content for Tab 1')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Tab 2',
|
|
||||||
active: () => activeTab() === 'tab2',
|
|
||||||
onclick: () => activeTab('tab2'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Content for Tab 2')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Tab 3',
|
|
||||||
active: () => activeTab() === 'tab3',
|
|
||||||
onclick: () => activeTab('tab3'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Content for Tab 3')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. With Icons
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const activeTab = $('home');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, ['🏠', 'Home']),
|
|
||||||
active: () => activeTab() === 'home',
|
|
||||||
onclick: () => activeTab('home'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Welcome to the Home tab!')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, ['⭐', 'Favorites']),
|
|
||||||
active: () => activeTab() === 'favorites',
|
|
||||||
onclick: () => activeTab('favorites'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Your favorite items appear here.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: Span({ class: 'flex items-center gap-2' }, ['⚙️', 'Settings']),
|
|
||||||
active: () => activeTab() === 'settings',
|
|
||||||
onclick: () => activeTab('settings'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Configure your preferences.')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. With Tooltips
|
|
||||||
const tooltipsTarget = document.querySelector('#demo-tooltips');
|
|
||||||
if (tooltipsTarget && !tooltipsTarget.hasChildNodes()) {
|
|
||||||
const TooltipsDemo = () => {
|
|
||||||
const activeTab = $('profile');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Profile',
|
|
||||||
tip: 'View your profile information',
|
|
||||||
active: () => activeTab() === 'profile',
|
|
||||||
onclick: () => activeTab('profile'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Profile information here.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Settings',
|
|
||||||
tip: 'Adjust your preferences',
|
|
||||||
active: () => activeTab() === 'settings',
|
|
||||||
onclick: () => activeTab('settings'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Settings configuration.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Notifications',
|
|
||||||
tip: 'Manage notifications',
|
|
||||||
active: () => activeTab() === 'notifications',
|
|
||||||
onclick: () => activeTab('notifications'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Notification settings.')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(TooltipsDemo, tooltipsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Disabled Tab
|
|
||||||
const disabledTarget = document.querySelector('#demo-disabled');
|
|
||||||
if (disabledTarget && !disabledTarget.hasChildNodes()) {
|
|
||||||
const DisabledDemo = () => {
|
|
||||||
const activeTab = $('basic');
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Basic',
|
|
||||||
active: () => activeTab() === 'basic',
|
|
||||||
onclick: () => activeTab('basic'),
|
|
||||||
content: Div({ class: 'p-4' }, 'Basic features available.')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Premium',
|
|
||||||
disabled: true,
|
|
||||||
tip: 'Upgrade to access',
|
|
||||||
content: Div({ class: 'p-4' }, 'Premium content (locked)')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Pro',
|
|
||||||
disabled: true,
|
|
||||||
tip: 'Coming soon',
|
|
||||||
content: Div({ class: 'p-4' }, 'Pro features (coming soon)')
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(DisabledDemo, disabledTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Reactive Content
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const activeTab = $('counter');
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
return Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Counter',
|
|
||||||
active: () => activeTab() === 'counter',
|
|
||||||
onclick: () => activeTab('counter'),
|
|
||||||
content: Div({ class: 'p-4 text-center' }, [
|
|
||||||
Div({ class: 'text-4xl font-bold mb-4' }, () => count()),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: () => count(count() + 1)
|
|
||||||
}, 'Increment')
|
|
||||||
])
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Timer',
|
|
||||||
active: () => activeTab() === 'timer',
|
|
||||||
onclick: () => activeTab('timer'),
|
|
||||||
content: Div({ class: 'p-4' }, () => `Current time: ${new Date().toLocaleTimeString()}`)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
active: () => activeTab() === 'status',
|
|
||||||
onclick: () => activeTab('status'),
|
|
||||||
content: Div({ class: 'p-4' }, () => `Counter value: ${count()}, Last updated: ${new Date().toLocaleTimeString()}`)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Form Tabs
|
|
||||||
const formTarget = document.querySelector('#demo-form');
|
|
||||||
if (formTarget && !formTarget.hasChildNodes()) {
|
|
||||||
const FormTabs = () => {
|
|
||||||
const activeTab = $('personal');
|
|
||||||
const formData = $({
|
|
||||||
name: '',
|
|
||||||
email: '',
|
|
||||||
address: '',
|
|
||||||
city: '',
|
|
||||||
notifications: true,
|
|
||||||
newsletter: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateField = (field, value) => {
|
|
||||||
formData({ ...formData(), [field]: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
Toast('Form submitted!', 'alert-success', 2000);
|
|
||||||
console.log(formData());
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Tabs({
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Personal Info',
|
|
||||||
active: () => activeTab() === 'personal',
|
|
||||||
onclick: () => activeTab('personal'),
|
|
||||||
content: Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Name',
|
|
||||||
value: () => formData().name,
|
|
||||||
placeholder: 'Enter your name',
|
|
||||||
oninput: (e) => updateField('name', e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: () => formData().email,
|
|
||||||
placeholder: 'email@example.com',
|
|
||||||
oninput: (e) => updateField('email', e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Address',
|
|
||||||
active: () => activeTab() === 'address',
|
|
||||||
onclick: () => activeTab('address'),
|
|
||||||
content: Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Address',
|
|
||||||
value: () => formData().address,
|
|
||||||
placeholder: 'Street address',
|
|
||||||
oninput: (e) => updateField('address', e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'City',
|
|
||||||
value: () => formData().city,
|
|
||||||
placeholder: 'City',
|
|
||||||
oninput: (e) => updateField('city', e.target.value)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Preferences',
|
|
||||||
active: () => activeTab() === 'prefs',
|
|
||||||
onclick: () => activeTab('prefs'),
|
|
||||||
content: Div({ class: 'p-4 space-y-4' }, [
|
|
||||||
Checkbox({
|
|
||||||
label: 'Email notifications',
|
|
||||||
value: () => formData().notifications,
|
|
||||||
onclick: () => updateField('notifications', !formData().notifications)
|
|
||||||
}),
|
|
||||||
Checkbox({
|
|
||||||
label: 'Newsletter subscription',
|
|
||||||
value: () => formData().newsletter,
|
|
||||||
onclick: () => updateField('newsletter', !formData().newsletter)
|
|
||||||
})
|
|
||||||
])
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
Div({ class: 'flex justify-end mt-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Submit')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormTabs, formTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const items = [
|
|
||||||
{ label: 'Tab 1', content: 'Content 1' },
|
|
||||||
{ label: 'Tab 2', content: 'Content 2' },
|
|
||||||
{ label: 'Tab 3', content: 'Content 3' }
|
|
||||||
].map((tab, idx) => ({
|
|
||||||
...tab,
|
|
||||||
active: () => idx === 0,
|
|
||||||
content: Div({ class: 'p-4' }, tab.content)
|
|
||||||
}));
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-6' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Default Tabs'),
|
|
||||||
Tabs({ items }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Boxed Tabs'),
|
|
||||||
Tabs({ items, class: 'tabs-box' }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Lifted Tabs'),
|
|
||||||
Tabs({ items, class: 'tabs-lifted' })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initTabsExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initTabsExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,553 +0,0 @@
|
|||||||
# Timeline
|
|
||||||
|
|
||||||
Timeline component for displaying chronological events, steps, or progress with customizable icons and content.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Timeline`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :-------------------------------------- | :--------------- | :----------------------------------------------- |
|
|
||||||
| `items` | `Array<TimelineItem> \| Signal` | `[]` | Timeline items to display |
|
|
||||||
| `vertical` | `boolean \| Signal<boolean>` | `true` | Vertical or horizontal orientation |
|
|
||||||
| `compact` | `boolean \| Signal<boolean>` | `false` | Compact mode with less padding |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
|
|
||||||
### TimelineItem Structure
|
|
||||||
|
|
||||||
| Property | Type | Description |
|
|
||||||
| :---------- | :--------------------------- | :----------------------------------------------- |
|
|
||||||
| `title` | `string \| VNode \| Signal` | Event title or main text |
|
|
||||||
| `detail` | `string \| VNode \| Signal` | Additional details or description |
|
|
||||||
| `icon` | `string \| VNode` | Custom icon (overrides type) |
|
|
||||||
| `type` | `string` | Type: 'success', 'warning', 'error', 'info' |
|
|
||||||
| `completed` | `boolean` | Whether event is completed (affects connector) |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Timeline
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const events = [
|
|
||||||
{ title: 'Project Started', detail: 'Initial planning and setup', type: 'info', completed: true },
|
|
||||||
{ title: 'Design Phase', detail: 'UI/UX design completed', type: 'success', completed: true },
|
|
||||||
{ title: 'Development', detail: 'Core features implemented', type: 'warning', completed: false },
|
|
||||||
{ title: 'Testing', detail: 'Quality assurance', type: 'info', completed: false },
|
|
||||||
{ title: 'Launch', detail: 'Production deployment', type: 'success', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({ items: events });
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Horizontal Timeline
|
|
||||||
|
|
||||||
<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-horizontal" class="bg-base-100 p-6 rounded-xl border border-base-300 overflow-x-auto"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const HorizontalDemo = () => {
|
|
||||||
const steps = [
|
|
||||||
{ title: 'Step 1', detail: 'Requirements', type: 'success', completed: true },
|
|
||||||
{ title: 'Step 2', detail: 'Design', type: 'success', completed: true },
|
|
||||||
{ title: 'Step 3', detail: 'Development', type: 'warning', completed: false },
|
|
||||||
{ title: 'Step 4', detail: 'Testing', type: 'info', completed: false },
|
|
||||||
{ title: 'Step 5', detail: 'Deploy', type: 'info', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({
|
|
||||||
items: steps,
|
|
||||||
vertical: false,
|
|
||||||
class: 'min-w-[600px]'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(HorizontalDemo, '#demo-horizontal');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compact Timeline
|
|
||||||
|
|
||||||
<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-compact" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CompactDemo = () => {
|
|
||||||
const activities = [
|
|
||||||
{ title: 'User login', detail: '10:30 AM', type: 'success', completed: true },
|
|
||||||
{ title: 'Viewed dashboard', detail: '10:32 AM', type: 'info', completed: true },
|
|
||||||
{ title: 'Updated profile', detail: '10:45 AM', type: 'success', completed: true },
|
|
||||||
{ title: 'Made purchase', detail: '11:00 AM', type: 'warning', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({
|
|
||||||
items: activities,
|
|
||||||
compact: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CompactDemo, '#demo-compact');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Icons
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const milestones = [
|
|
||||||
{ title: 'Kickoff', detail: 'Project kickoff meeting', icon: Icons.iconInfo, completed: true },
|
|
||||||
{ title: 'MVP', detail: 'Minimum viable product', icon: Icons.iconSuccess, completed: true },
|
|
||||||
{ title: 'Beta', detail: 'Beta release', icon: Icons.iconWarning, completed: false },
|
|
||||||
{ title: 'Launch', detail: 'Public launch', icon: Icons.iconShow, completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({ items: milestones });
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactive Timeline
|
|
||||||
|
|
||||||
<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 currentStep = $(2);
|
|
||||||
const steps = [
|
|
||||||
{ title: 'Order Placed', detail: 'Your order has been confirmed', type: 'success', completed: true },
|
|
||||||
{ title: 'Processing', detail: 'Payment verified, preparing shipment', type: 'success', completed: currentStep() > 1 },
|
|
||||||
{ title: 'Shipped', detail: 'Package on the way', type: 'warning', completed: currentStep() > 2 },
|
|
||||||
{ title: 'Delivered', detail: 'Arriving soon', type: 'info', completed: currentStep() > 3 }
|
|
||||||
];
|
|
||||||
|
|
||||||
const items = () => steps.map((step, idx) => ({
|
|
||||||
...step,
|
|
||||||
completed: idx < currentStep()
|
|
||||||
}));
|
|
||||||
|
|
||||||
const nextStep = () => {
|
|
||||||
if (currentStep() < steps.length) {
|
|
||||||
currentStep(currentStep() + 1);
|
|
||||||
Toast(`Step ${currentStep()}: ${steps[currentStep() - 1].title}`, 'alert-info', 1500);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const reset = () => {
|
|
||||||
currentStep(0);
|
|
||||||
Toast('Order tracking reset', 'alert-warning', 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Timeline({ items: items }),
|
|
||||||
Div({ class: 'flex gap-2 justify-center mt-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-sm',
|
|
||||||
onclick: nextStep,
|
|
||||||
disabled: () => currentStep() >= steps.length
|
|
||||||
}, 'Next Step'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm',
|
|
||||||
onclick: reset
|
|
||||||
}, 'Reset')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, '#demo-reactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Order Status Tracker
|
|
||||||
|
|
||||||
<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-order" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const OrderDemo = () => {
|
|
||||||
const status = $('shipped');
|
|
||||||
|
|
||||||
const statusMap = {
|
|
||||||
pending: { title: 'Order Pending', detail: 'Awaiting confirmation', completed: false, type: 'warning' },
|
|
||||||
confirmed: { title: 'Order Confirmed', detail: 'Payment received', completed: false, type: 'info' },
|
|
||||||
processing: { title: 'Processing', detail: 'Preparing your order', completed: false, type: 'info' },
|
|
||||||
shipped: { title: 'Shipped', detail: 'Package in transit', completed: false, type: 'info' },
|
|
||||||
delivered: { title: 'Delivered', detail: 'Order completed', completed: false, type: 'success' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusOrder = ['pending', 'confirmed', 'processing', 'shipped', 'delivered'];
|
|
||||||
const currentIndex = statusOrder.indexOf(status());
|
|
||||||
|
|
||||||
const items = statusOrder.map((key, idx) => ({
|
|
||||||
...statusMap[key],
|
|
||||||
completed: idx < currentIndex
|
|
||||||
}));
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Timeline({ items, compact: true }),
|
|
||||||
Div({ class: 'flex gap-2 justify-center flex-wrap mt-4' }, statusOrder.map(s =>
|
|
||||||
Button({
|
|
||||||
class: `btn btn-xs ${status() === s ? 'btn-primary' : 'btn-ghost'}`,
|
|
||||||
onclick: () => status(s)
|
|
||||||
}, statusMap[s].title)
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(OrderDemo, '#demo-order');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Company History
|
|
||||||
|
|
||||||
<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-history" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const HistoryDemo = () => {
|
|
||||||
const milestones = [
|
|
||||||
{
|
|
||||||
title: '2015 - Founded',
|
|
||||||
detail: 'Company started with 3 employees in a small office',
|
|
||||||
type: 'success',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2017 - First Product',
|
|
||||||
detail: 'Launched our first software product to market',
|
|
||||||
type: 'success',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2019 - Series A',
|
|
||||||
detail: 'Raised $5M in funding, expanded team to 50',
|
|
||||||
type: 'success',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2022 - Global Expansion',
|
|
||||||
detail: 'Opened offices in Europe and Asia',
|
|
||||||
type: 'info',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2024 - AI Integration',
|
|
||||||
detail: 'Launched AI-powered features',
|
|
||||||
type: 'warning',
|
|
||||||
completed: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2026 - Future Goals',
|
|
||||||
detail: 'Aiming for market leadership',
|
|
||||||
type: 'info',
|
|
||||||
completed: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({ items: milestones });
|
|
||||||
};
|
|
||||||
$mount(HistoryDemo, '#demo-history');
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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-8"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const sampleItems = [
|
|
||||||
{ title: 'Event 1', detail: 'Description here', type: 'success', completed: true },
|
|
||||||
{ title: 'Event 2', detail: 'Description here', type: 'warning', completed: false },
|
|
||||||
{ title: 'Event 3', detail: 'Description here', type: 'info', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-8' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Vertical Timeline'),
|
|
||||||
Timeline({ items: sampleItems }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Horizontal Timeline'),
|
|
||||||
Timeline({ items: sampleItems, vertical: false, class: 'min-w-[500px]' }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Compact Timeline'),
|
|
||||||
Timeline({ items: sampleItems, compact: true })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, '#demo-variants');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initTimelineExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Timeline
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
const events = [
|
|
||||||
{ title: 'Project Started', detail: 'Initial planning and setup', type: 'info', completed: true },
|
|
||||||
{ title: 'Design Phase', detail: 'UI/UX design completed', type: 'success', completed: true },
|
|
||||||
{ title: 'Development', detail: 'Core features implemented', type: 'warning', completed: false },
|
|
||||||
{ title: 'Testing', detail: 'Quality assurance', type: 'info', completed: false },
|
|
||||||
{ title: 'Launch', detail: 'Production deployment', type: 'success', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({ items: events });
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Horizontal Timeline
|
|
||||||
const horizontalTarget = document.querySelector('#demo-horizontal');
|
|
||||||
if (horizontalTarget && !horizontalTarget.hasChildNodes()) {
|
|
||||||
const HorizontalDemo = () => {
|
|
||||||
const steps = [
|
|
||||||
{ title: 'Step 1', detail: 'Requirements', type: 'success', completed: true },
|
|
||||||
{ title: 'Step 2', detail: 'Design', type: 'success', completed: true },
|
|
||||||
{ title: 'Step 3', detail: 'Development', type: 'warning', completed: false },
|
|
||||||
{ title: 'Step 4', detail: 'Testing', type: 'info', completed: false },
|
|
||||||
{ title: 'Step 5', detail: 'Deploy', type: 'info', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({
|
|
||||||
items: steps,
|
|
||||||
vertical: false,
|
|
||||||
class: 'min-w-[600px]'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(HorizontalDemo, horizontalTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Compact Timeline
|
|
||||||
const compactTarget = document.querySelector('#demo-compact');
|
|
||||||
if (compactTarget && !compactTarget.hasChildNodes()) {
|
|
||||||
const CompactDemo = () => {
|
|
||||||
const activities = [
|
|
||||||
{ title: 'User login', detail: '10:30 AM', type: 'success', completed: true },
|
|
||||||
{ title: 'Viewed dashboard', detail: '10:32 AM', type: 'info', completed: true },
|
|
||||||
{ title: 'Updated profile', detail: '10:45 AM', type: 'success', completed: true },
|
|
||||||
{ title: 'Made purchase', detail: '11:00 AM', type: 'warning', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({
|
|
||||||
items: activities,
|
|
||||||
compact: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
$mount(CompactDemo, compactTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Custom Icons
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
const milestones = [
|
|
||||||
{ title: 'Kickoff', detail: 'Project kickoff meeting', icon: Icons.iconInfo, completed: true },
|
|
||||||
{ title: 'MVP', detail: 'Minimum viable product', icon: Icons.iconSuccess, completed: true },
|
|
||||||
{ title: 'Beta', detail: 'Beta release', icon: Icons.iconWarning, completed: false },
|
|
||||||
{ title: 'Launch', detail: 'Public launch', icon: Icons.iconShow, completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({ items: milestones });
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Reactive Timeline
|
|
||||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
|
||||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
|
||||||
const ReactiveDemo = () => {
|
|
||||||
const currentStep = $(2);
|
|
||||||
const steps = [
|
|
||||||
{ title: 'Order Placed', detail: 'Your order has been confirmed', type: 'success', completed: true },
|
|
||||||
{ title: 'Processing', detail: 'Payment verified, preparing shipment', type: 'success', completed: currentStep() > 1 },
|
|
||||||
{ title: 'Shipped', detail: 'Package on the way', type: 'warning', completed: currentStep() > 2 },
|
|
||||||
{ title: 'Delivered', detail: 'Arriving soon', type: 'info', completed: currentStep() > 3 }
|
|
||||||
];
|
|
||||||
|
|
||||||
const items = () => steps.map((step, idx) => ({
|
|
||||||
...step,
|
|
||||||
completed: idx < currentStep()
|
|
||||||
}));
|
|
||||||
|
|
||||||
const nextStep = () => {
|
|
||||||
if (currentStep() < steps.length) {
|
|
||||||
currentStep(currentStep() + 1);
|
|
||||||
Toast(`Step ${currentStep()}: ${steps[currentStep() - 1].title}`, 'alert-info', 1500);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const reset = () => {
|
|
||||||
currentStep(0);
|
|
||||||
Toast('Order tracking reset', 'alert-warning', 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Timeline({ items: items }),
|
|
||||||
Div({ class: 'flex gap-2 justify-center mt-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-sm',
|
|
||||||
onclick: nextStep,
|
|
||||||
disabled: () => currentStep() >= steps.length
|
|
||||||
}, 'Next Step'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-ghost btn-sm',
|
|
||||||
onclick: reset
|
|
||||||
}, 'Reset')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ReactiveDemo, reactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Order Status Tracker
|
|
||||||
const orderTarget = document.querySelector('#demo-order');
|
|
||||||
if (orderTarget && !orderTarget.hasChildNodes()) {
|
|
||||||
const OrderDemo = () => {
|
|
||||||
const status = $('shipped');
|
|
||||||
|
|
||||||
const statusMap = {
|
|
||||||
pending: { title: 'Order Pending', detail: 'Awaiting confirmation', completed: false, type: 'warning' },
|
|
||||||
confirmed: { title: 'Order Confirmed', detail: 'Payment received', completed: false, type: 'info' },
|
|
||||||
processing: { title: 'Processing', detail: 'Preparing your order', completed: false, type: 'info' },
|
|
||||||
shipped: { title: 'Shipped', detail: 'Package in transit', completed: false, type: 'info' },
|
|
||||||
delivered: { title: 'Delivered', detail: 'Order completed', completed: false, type: 'success' }
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusOrder = ['pending', 'confirmed', 'processing', 'shipped', 'delivered'];
|
|
||||||
const currentIndex = statusOrder.indexOf(status());
|
|
||||||
|
|
||||||
const items = statusOrder.map((key, idx) => ({
|
|
||||||
...statusMap[key],
|
|
||||||
completed: idx < currentIndex
|
|
||||||
}));
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Timeline({ items, compact: true }),
|
|
||||||
Div({ class: 'flex gap-2 justify-center flex-wrap mt-4' }, statusOrder.map(s =>
|
|
||||||
Button({
|
|
||||||
class: `btn btn-xs ${status() === s ? 'btn-primary' : 'btn-ghost'}`,
|
|
||||||
onclick: () => status(s)
|
|
||||||
}, statusMap[s].title)
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(OrderDemo, orderTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Company History
|
|
||||||
const historyTarget = document.querySelector('#demo-history');
|
|
||||||
if (historyTarget && !historyTarget.hasChildNodes()) {
|
|
||||||
const HistoryDemo = () => {
|
|
||||||
const milestones = [
|
|
||||||
{
|
|
||||||
title: '2015 - Founded',
|
|
||||||
detail: 'Company started with 3 employees in a small office',
|
|
||||||
type: 'success',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2017 - First Product',
|
|
||||||
detail: 'Launched our first software product to market',
|
|
||||||
type: 'success',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2019 - Series A',
|
|
||||||
detail: 'Raised $5M in funding, expanded team to 50',
|
|
||||||
type: 'success',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2022 - Global Expansion',
|
|
||||||
detail: 'Opened offices in Europe and Asia',
|
|
||||||
type: 'info',
|
|
||||||
completed: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2024 - AI Integration',
|
|
||||||
detail: 'Launched AI-powered features',
|
|
||||||
type: 'warning',
|
|
||||||
completed: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '2026 - Future Goals',
|
|
||||||
detail: 'Aiming for market leadership',
|
|
||||||
type: 'info',
|
|
||||||
completed: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return Timeline({ items: milestones });
|
|
||||||
};
|
|
||||||
$mount(HistoryDemo, historyTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Variants
|
|
||||||
const variantsTarget = document.querySelector('#demo-variants');
|
|
||||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
|
||||||
const VariantsDemo = () => {
|
|
||||||
const sampleItems = [
|
|
||||||
{ title: 'Event 1', detail: 'Description here', type: 'success', completed: true },
|
|
||||||
{ title: 'Event 2', detail: 'Description here', type: 'warning', completed: false },
|
|
||||||
{ title: 'Event 3', detail: 'Description here', type: 'info', completed: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-8' }, [
|
|
||||||
Div({ class: 'text-sm font-bold' }, 'Vertical Timeline'),
|
|
||||||
Timeline({ items: sampleItems }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Horizontal Timeline'),
|
|
||||||
Timeline({ items: sampleItems, vertical: false, class: 'min-w-[500px]' }),
|
|
||||||
|
|
||||||
Div({ class: 'text-sm font-bold mt-4' }, 'Compact Timeline'),
|
|
||||||
Timeline({ items: sampleItems, compact: true })
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(VariantsDemo, variantsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initTimelineExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initTimelineExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,630 +0,0 @@
|
|||||||
# Toast
|
|
||||||
|
|
||||||
Toast notification utility for displaying temporary messages that automatically dismiss after a specified duration. Can be used programmatically.
|
|
||||||
|
|
||||||
## Function
|
|
||||||
|
|
||||||
`Toast(message, type = 'alert-info', duration = 3500)`
|
|
||||||
|
|
||||||
| Param | Type | Default | Description |
|
|
||||||
| :--------- | :--------------------------- | :--------------- | :----------------------------------------------- |
|
|
||||||
| `message` | `string \| VNode` | `-` | Message content to display |
|
|
||||||
| `type` | `string` | `'alert-info'` | Alert type: 'alert-info', 'alert-success', 'alert-warning', 'alert-error' |
|
|
||||||
| `duration` | `number` | `3500` | Auto-dismiss duration in milliseconds |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic Toast
|
|
||||||
|
|
||||||
<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-wrap gap-2 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-info',
|
|
||||||
onclick: () => Toast('This is an info message', 'alert-info', 3000)
|
|
||||||
}, 'Info Toast'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-success',
|
|
||||||
onclick: () => Toast('Operation successful!', 'alert-success', 3000)
|
|
||||||
}, 'Success Toast'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-warning',
|
|
||||||
onclick: () => Toast('Please check your input', 'alert-warning', 3000)
|
|
||||||
}, 'Warning Toast'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: () => Toast('An error occurred', 'alert-error', 3000)
|
|
||||||
}, 'Error Toast')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Different Durations
|
|
||||||
|
|
||||||
<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-duration" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-2 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const DurationDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Short (1s)', 'alert-info', 1000)
|
|
||||||
}, '1 Second'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Normal (3s)', 'alert-success', 3000)
|
|
||||||
}, '3 Seconds'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Long (5s)', 'alert-warning', 5000)
|
|
||||||
}, '5 Seconds'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Very Long (8s)', 'alert-error', 8000)
|
|
||||||
}, '8 Seconds')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DurationDemo, '#demo-duration');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive Toast
|
|
||||||
|
|
||||||
<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-interactive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
const showRandomToast = () => {
|
|
||||||
const types = ['alert-info', 'alert-success', 'alert-warning', 'alert-error'];
|
|
||||||
const messages = [
|
|
||||||
'You clicked the button!',
|
|
||||||
'Action completed successfully',
|
|
||||||
'Processing your request...',
|
|
||||||
'Something interesting happened'
|
|
||||||
];
|
|
||||||
const randomType = types[Math.floor(Math.random() * types.length)];
|
|
||||||
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
|
|
||||||
count(count() + 1);
|
|
||||||
Toast(`${randomMessage} (${count()})`, randomType, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: showRandomToast
|
|
||||||
}, 'Show Random Toast'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `Toasts shown: ${count()}`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Form Validation Toast
|
|
||||||
|
|
||||||
<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 FormToastDemo = () => {
|
|
||||||
const email = $('');
|
|
||||||
const password = $('');
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (!email()) {
|
|
||||||
Toast('Please enter your email', 'alert-warning', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!email().includes('@')) {
|
|
||||||
Toast('Please enter a valid email address', 'alert-error', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!password()) {
|
|
||||||
Toast('Please enter your password', 'alert-warning', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (password().length < 6) {
|
|
||||||
Toast('Password must be at least 6 characters', 'alert-error', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Toast('Login successful! Redirecting...', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 max-w-md mx-auto' }, [
|
|
||||||
Div({ class: 'text-lg font-bold text-center' }, 'Login Form'),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'user@example.com',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Password',
|
|
||||||
type: 'password',
|
|
||||||
value: password,
|
|
||||||
placeholder: 'Enter password',
|
|
||||||
oninput: (e) => password(e.target.value)
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Login')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormToastDemo, '#demo-form');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Success Feedback
|
|
||||||
|
|
||||||
<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-feedback" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const FeedbackDemo = () => {
|
|
||||||
const items = $([
|
|
||||||
{ id: 1, name: 'Item 1', saved: false },
|
|
||||||
{ id: 2, name: 'Item 2', saved: false },
|
|
||||||
{ id: 3, name: 'Item 3', saved: false }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const saveItem = (id) => {
|
|
||||||
items(items().map(item =>
|
|
||||||
item.id === id ? { ...item, saved: true } : item
|
|
||||||
));
|
|
||||||
Toast(`Item ${id} saved successfully!`, 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveAll = () => {
|
|
||||||
items(items().map(item => ({ ...item, saved: true })));
|
|
||||||
Toast('All items saved!', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'font-bold' }, 'Items to Save'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-primary',
|
|
||||||
onclick: saveAll
|
|
||||||
}, 'Save All')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
|
|
||||||
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({}, item.name),
|
|
||||||
item.saved
|
|
||||||
? Span({ class: 'badge badge-success' }, '✓ Saved')
|
|
||||||
: Button({
|
|
||||||
class: 'btn btn-xs btn-primary',
|
|
||||||
onclick: () => saveItem(item.id)
|
|
||||||
}, 'Save')
|
|
||||||
])
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FeedbackDemo, '#demo-feedback');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
<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"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ErrorDemo = () => {
|
|
||||||
const simulateApiCall = () => {
|
|
||||||
const success = Math.random() > 0.3;
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
Toast('Data loaded successfully!', 'alert-success', 2000);
|
|
||||||
} else {
|
|
||||||
Toast('Failed to load data. Please try again.', 'alert-error', 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const simulateNetworkError = () => {
|
|
||||||
Toast('Network error: Unable to connect to server', 'alert-error', 4000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const simulateTimeout = () => {
|
|
||||||
Toast('Request timeout (5s). Please check your connection.', 'alert-warning', 4000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-3 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: simulateApiCall
|
|
||||||
}, 'Simulate API Call'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: simulateNetworkError
|
|
||||||
}, 'Network Error'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-warning',
|
|
||||||
onclick: simulateTimeout
|
|
||||||
}, 'Timeout')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ErrorDemo, '#demo-error');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom Messages
|
|
||||||
|
|
||||||
<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-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-2 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const showCustomToast = (type, message) => {
|
|
||||||
Toast(message, type, 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-info',
|
|
||||||
onclick: () => showCustomToast('alert-info', '📧 New email received from john@example.com')
|
|
||||||
}, 'Email'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-success',
|
|
||||||
onclick: () => showCustomToast('alert-success', '💰 Payment of $49.99 completed')
|
|
||||||
}, 'Payment'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-warning',
|
|
||||||
onclick: () => showCustomToast('alert-warning', '⚠️ Your session will expire in 5 minutes')
|
|
||||||
}, 'Session Warning'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: () => showCustomToast('alert-error', '🔒 Failed login attempt detected')
|
|
||||||
}, 'Security Alert')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, '#demo-custom');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple Toasts
|
|
||||||
|
|
||||||
<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 showMultipleToasts = () => {
|
|
||||||
Toast('First message', 'alert-info', 3000);
|
|
||||||
setTimeout(() => Toast('Second message', 'alert-success', 3000), 500);
|
|
||||||
setTimeout(() => Toast('Third message', 'alert-warning', 3000), 1000);
|
|
||||||
setTimeout(() => Toast('Fourth message', 'alert-error', 3000), 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: showMultipleToasts
|
|
||||||
}, 'Show Multiple Toasts')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MultipleDemo, '#demo-multiple');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initToastExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Toast
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-info',
|
|
||||||
onclick: () => Toast('This is an info message', 'alert-info', 3000)
|
|
||||||
}, 'Info Toast'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-success',
|
|
||||||
onclick: () => Toast('Operation successful!', 'alert-success', 3000)
|
|
||||||
}, 'Success Toast'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-warning',
|
|
||||||
onclick: () => Toast('Please check your input', 'alert-warning', 3000)
|
|
||||||
}, 'Warning Toast'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: () => Toast('An error occurred', 'alert-error', 3000)
|
|
||||||
}, 'Error Toast')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Different Durations
|
|
||||||
const durationTarget = document.querySelector('#demo-duration');
|
|
||||||
if (durationTarget && !durationTarget.hasChildNodes()) {
|
|
||||||
const DurationDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Short (1s)', 'alert-info', 1000)
|
|
||||||
}, '1 Second'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Normal (3s)', 'alert-success', 3000)
|
|
||||||
}, '3 Seconds'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Long (5s)', 'alert-warning', 5000)
|
|
||||||
}, '5 Seconds'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm',
|
|
||||||
onclick: () => Toast('Very Long (8s)', 'alert-error', 8000)
|
|
||||||
}, '8 Seconds')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(DurationDemo, durationTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Interactive Toast
|
|
||||||
const interactiveTarget = document.querySelector('#demo-interactive');
|
|
||||||
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const count = $(0);
|
|
||||||
|
|
||||||
const showRandomToast = () => {
|
|
||||||
const types = ['alert-info', 'alert-success', 'alert-warning', 'alert-error'];
|
|
||||||
const messages = [
|
|
||||||
'You clicked the button!',
|
|
||||||
'Action completed successfully',
|
|
||||||
'Processing your request...',
|
|
||||||
'Something interesting happened'
|
|
||||||
];
|
|
||||||
const randomType = types[Math.floor(Math.random() * types.length)];
|
|
||||||
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
|
|
||||||
count(count() + 1);
|
|
||||||
Toast(`${randomMessage} (${count()})`, randomType, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: showRandomToast
|
|
||||||
}, 'Show Random Toast'),
|
|
||||||
Div({ class: 'text-sm opacity-70' }, () => `Toasts shown: ${count()}`)
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, interactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Form Validation Toast
|
|
||||||
const formTarget = document.querySelector('#demo-form');
|
|
||||||
if (formTarget && !formTarget.hasChildNodes()) {
|
|
||||||
const FormToastDemo = () => {
|
|
||||||
const email = $('');
|
|
||||||
const password = $('');
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (!email()) {
|
|
||||||
Toast('Please enter your email', 'alert-warning', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!email().includes('@')) {
|
|
||||||
Toast('Please enter a valid email address', 'alert-error', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!password()) {
|
|
||||||
Toast('Please enter your password', 'alert-warning', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (password().length < 6) {
|
|
||||||
Toast('Password must be at least 6 characters', 'alert-error', 3000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Toast('Login successful! Redirecting...', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 max-w-md mx-auto' }, [
|
|
||||||
Div({ class: 'text-lg font-bold text-center' }, 'Login Form'),
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'user@example.com',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
}),
|
|
||||||
Input({
|
|
||||||
label: 'Password',
|
|
||||||
type: 'password',
|
|
||||||
value: password,
|
|
||||||
placeholder: 'Enter password',
|
|
||||||
oninput: (e) => password(e.target.value)
|
|
||||||
}),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: handleSubmit
|
|
||||||
}, 'Login')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormToastDemo, formTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Success Feedback
|
|
||||||
const feedbackTarget = document.querySelector('#demo-feedback');
|
|
||||||
if (feedbackTarget && !feedbackTarget.hasChildNodes()) {
|
|
||||||
const FeedbackDemo = () => {
|
|
||||||
const items = $([
|
|
||||||
{ id: 1, name: 'Item 1', saved: false },
|
|
||||||
{ id: 2, name: 'Item 2', saved: false },
|
|
||||||
{ id: 3, name: 'Item 3', saved: false }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const saveItem = (id) => {
|
|
||||||
items(items().map(item =>
|
|
||||||
item.id === id ? { ...item, saved: true } : item
|
|
||||||
));
|
|
||||||
Toast(`Item ${id} saved successfully!`, 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveAll = () => {
|
|
||||||
items(items().map(item => ({ ...item, saved: true })));
|
|
||||||
Toast('All items saved!', 'alert-success', 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4' }, [
|
|
||||||
Div({ class: 'flex justify-between items-center' }, [
|
|
||||||
Span({ class: 'font-bold' }, 'Items to Save'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-sm btn-primary',
|
|
||||||
onclick: saveAll
|
|
||||||
}, 'Save All')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
|
|
||||||
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
|
|
||||||
Span({}, item.name),
|
|
||||||
item.saved
|
|
||||||
? Span({ class: 'badge badge-success' }, '✓ Saved')
|
|
||||||
: Button({
|
|
||||||
class: 'btn btn-xs btn-primary',
|
|
||||||
onclick: () => saveItem(item.id)
|
|
||||||
}, 'Save')
|
|
||||||
])
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FeedbackDemo, feedbackTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Error Handling
|
|
||||||
const errorTarget = document.querySelector('#demo-error');
|
|
||||||
if (errorTarget && !errorTarget.hasChildNodes()) {
|
|
||||||
const ErrorDemo = () => {
|
|
||||||
const simulateApiCall = () => {
|
|
||||||
const success = Math.random() > 0.3;
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
Toast('Data loaded successfully!', 'alert-success', 2000);
|
|
||||||
} else {
|
|
||||||
Toast('Failed to load data. Please try again.', 'alert-error', 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const simulateNetworkError = () => {
|
|
||||||
Toast('Network error: Unable to connect to server', 'alert-error', 4000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const simulateTimeout = () => {
|
|
||||||
Toast('Request timeout (5s). Please check your connection.', 'alert-warning', 4000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-3 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: simulateApiCall
|
|
||||||
}, 'Simulate API Call'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: simulateNetworkError
|
|
||||||
}, 'Network Error'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-warning',
|
|
||||||
onclick: simulateTimeout
|
|
||||||
}, 'Timeout')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ErrorDemo, errorTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Custom Messages
|
|
||||||
const customTarget = document.querySelector('#demo-custom');
|
|
||||||
if (customTarget && !customTarget.hasChildNodes()) {
|
|
||||||
const CustomDemo = () => {
|
|
||||||
const showCustomToast = (type, message) => {
|
|
||||||
Toast(message, type, 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-info',
|
|
||||||
onclick: () => showCustomToast('alert-info', '📧 New email received from john@example.com')
|
|
||||||
}, 'Email'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-success',
|
|
||||||
onclick: () => showCustomToast('alert-success', '💰 Payment of $49.99 completed')
|
|
||||||
}, 'Payment'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-warning',
|
|
||||||
onclick: () => showCustomToast('alert-warning', '⚠️ Your session will expire in 5 minutes')
|
|
||||||
}, 'Session Warning'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-error',
|
|
||||||
onclick: () => showCustomToast('alert-error', '🔒 Failed login attempt detected')
|
|
||||||
}, 'Security Alert')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(CustomDemo, customTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Multiple Toasts
|
|
||||||
const multipleTarget = document.querySelector('#demo-multiple');
|
|
||||||
if (multipleTarget && !multipleTarget.hasChildNodes()) {
|
|
||||||
const MultipleDemo = () => {
|
|
||||||
const showMultipleToasts = () => {
|
|
||||||
Toast('First message', 'alert-info', 3000);
|
|
||||||
setTimeout(() => Toast('Second message', 'alert-success', 3000), 500);
|
|
||||||
setTimeout(() => Toast('Third message', 'alert-warning', 3000), 1000);
|
|
||||||
setTimeout(() => Toast('Fourth message', 'alert-error', 3000), 1500);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex justify-center' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary',
|
|
||||||
onclick: showMultipleToasts
|
|
||||||
}, 'Show Multiple Toasts')
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(MultipleDemo, multipleTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initToastExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initToastExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -1,554 +0,0 @@
|
|||||||
# Tooltip
|
|
||||||
|
|
||||||
Tooltip component for displaying helpful hints and additional information on hover.
|
|
||||||
|
|
||||||
## Tag
|
|
||||||
|
|
||||||
`Tooltip`
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| Prop | Type | Default | Description |
|
|
||||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
|
||||||
| `tip` | `string \| VNode \| Signal` | `-` | Tooltip content to display on hover |
|
|
||||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
|
||||||
| `children` | `VNode` | `-` | Element to attach the tooltip to |
|
|
||||||
|
|
||||||
## Live Examples
|
|
||||||
|
|
||||||
### Basic 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-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'This is a tooltip' }, [
|
|
||||||
Button({ class: 'btn btn-primary' }, 'Hover me')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Tooltips can be placed on any element' }, [
|
|
||||||
Span({ class: 'text-sm cursor-help border-b border-dashed' }, 'Help text')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Icons can also have tooltips' }, [
|
|
||||||
Icons.iconInfo
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, '#demo-basic');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tooltip Positions
|
|
||||||
|
|
||||||
<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-positions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const PositionsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'Top tooltip', class: 'tooltip-top' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Top')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Bottom tooltip', class: 'tooltip-bottom' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Bottom')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Left tooltip', class: 'tooltip-left' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Left')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Right tooltip', class: 'tooltip-right' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Right')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PositionsDemo, '#demo-positions');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tooltip with Icons
|
|
||||||
|
|
||||||
<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-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-8 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'Save document' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '💾')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Edit item' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '✏️')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Delete permanently' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle text-error' }, '🗑️')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Settings' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '⚙️')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, '#demo-icons');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Form Field Tooltips
|
|
||||||
|
|
||||||
<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 username = $('');
|
|
||||||
const email = $('');
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 max-w-md mx-auto' }, [
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Username',
|
|
||||||
value: username,
|
|
||||||
placeholder: 'Choose a username',
|
|
||||||
oninput: (e) => username(e.target.value)
|
|
||||||
}),
|
|
||||||
Tooltip({ tip: 'Must be at least 3 characters, letters and numbers only' }, [
|
|
||||||
Span({ class: 'cursor-help text-info' }, '?')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'Enter your email',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
}),
|
|
||||||
Tooltip({ tip: 'We\'ll never share your email with anyone' }, [
|
|
||||||
Span({ class: 'cursor-help text-info' }, '?')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormDemo, '#demo-form');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interactive 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-interactive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const showTip = $(false);
|
|
||||||
const tooltipText = $('Hover over the button!');
|
|
||||||
|
|
||||||
const updateTooltip = (text) => {
|
|
||||||
tooltipText(text);
|
|
||||||
setTimeout(() => {
|
|
||||||
tooltipText('Hover over the button!');
|
|
||||||
}, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Tooltip({ tip: () => tooltipText() }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-lg',
|
|
||||||
onclick: () => Toast('Button clicked!', 'alert-info', 2000)
|
|
||||||
}, 'Interactive Button')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2 flex-wrap justify-center mt-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs',
|
|
||||||
onclick: () => updateTooltip('You clicked the button!')
|
|
||||||
}, 'Change Tooltip'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs',
|
|
||||||
onclick: () => updateTooltip('Try hovering now!')
|
|
||||||
}, 'Change Again')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, '#demo-interactive');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rich Tooltip Content
|
|
||||||
|
|
||||||
<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-rich" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const RichDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Tooltip({
|
|
||||||
tip: Div({ class: 'text-left p-1' }, [
|
|
||||||
Div({ class: 'font-bold' }, 'User Info'),
|
|
||||||
Div({ class: 'text-xs' }, 'John Doe'),
|
|
||||||
Div({ class: 'text-xs' }, 'john@example.com'),
|
|
||||||
Div({ class: 'badge badge-xs badge-primary mt-1' }, 'Admin')
|
|
||||||
])
|
|
||||||
}, [
|
|
||||||
Button({ class: 'btn btn-outline' }, 'User Profile')
|
|
||||||
]),
|
|
||||||
Tooltip({
|
|
||||||
tip: Div({ class: 'text-left p-1' }, [
|
|
||||||
Div({ class: 'font-bold flex items-center gap-1' }, [
|
|
||||||
Icons.iconWarning,
|
|
||||||
Span({}, 'System Status')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-xs' }, 'All systems operational'),
|
|
||||||
Div({ class: 'text-xs text-success' }, '✓ 99.9% uptime')
|
|
||||||
])
|
|
||||||
}, [
|
|
||||||
Button({ class: 'btn btn-outline' }, 'System Status')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(RichDemo, '#demo-rich');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Color 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-colors" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4 justify-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const ColorsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'Primary tooltip', class: 'tooltip-primary' }, [
|
|
||||||
Button({ class: 'btn btn-primary btn-sm' }, 'Primary')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Secondary tooltip', class: 'tooltip-secondary' }, [
|
|
||||||
Button({ class: 'btn btn-secondary btn-sm' }, 'Secondary')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Accent tooltip', class: 'tooltip-accent' }, [
|
|
||||||
Button({ class: 'btn btn-accent btn-sm' }, 'Accent')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Info tooltip', class: 'tooltip-info' }, [
|
|
||||||
Button({ class: 'btn btn-info btn-sm' }, 'Info')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Success tooltip', class: 'tooltip-success' }, [
|
|
||||||
Button({ class: 'btn btn-success btn-sm' }, 'Success')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Warning tooltip', class: 'tooltip-warning' }, [
|
|
||||||
Button({ class: 'btn btn-warning btn-sm' }, 'Warning')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Error tooltip', class: 'tooltip-error' }, [
|
|
||||||
Button({ class: 'btn btn-error btn-sm' }, 'Error')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ColorsDemo, '#demo-colors');
|
|
||||||
```
|
|
||||||
|
|
||||||
### All Tooltip Positions
|
|
||||||
|
|
||||||
<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-all-positions" class="bg-base-100 p-6 rounded-xl border border-base-300 grid grid-cols-3 gap-4 justify-items-center"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const AllPositionsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-3 gap-4 justify-items-center' }, [
|
|
||||||
Div({ class: 'col-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Top tooltip', class: 'tooltip-top' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Top')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-1 row-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Left tooltip', class: 'tooltip-left' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Left')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-2 row-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Center tooltip', class: 'tooltip' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Center')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-3 row-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Right tooltip', class: 'tooltip-right' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Right')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-2 row-start-3' }, [
|
|
||||||
Tooltip({ tip: 'Bottom tooltip', class: 'tooltip-bottom' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Bottom')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AllPositionsDemo, '#demo-all-positions');
|
|
||||||
```
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
const initTooltipExamples = () => {
|
|
||||||
|
|
||||||
// 1. Basic Tooltip
|
|
||||||
const basicTarget = document.querySelector('#demo-basic');
|
|
||||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
|
||||||
const BasicDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'This is a tooltip' }, [
|
|
||||||
Button({ class: 'btn btn-primary' }, 'Hover me')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Tooltips can be placed on any element' }, [
|
|
||||||
Span({ class: 'text-sm cursor-help border-b border-dashed' }, 'Help text')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Icons can also have tooltips' }, [
|
|
||||||
Icons.iconInfo
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(BasicDemo, basicTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Tooltip Positions
|
|
||||||
const positionsTarget = document.querySelector('#demo-positions');
|
|
||||||
if (positionsTarget && !positionsTarget.hasChildNodes()) {
|
|
||||||
const PositionsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'Top tooltip', class: 'tooltip-top' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Top')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Bottom tooltip', class: 'tooltip-bottom' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Bottom')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Left tooltip', class: 'tooltip-left' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Left')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Right tooltip', class: 'tooltip-right' }, [
|
|
||||||
Button({ class: 'btn btn-sm' }, 'Right')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(PositionsDemo, positionsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Tooltip with Icons
|
|
||||||
const iconsTarget = document.querySelector('#demo-icons');
|
|
||||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
|
||||||
const IconsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'Save document' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '💾')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Edit item' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '✏️')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Delete permanently' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle text-error' }, '🗑️')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Settings' }, [
|
|
||||||
Button({ class: 'btn btn-ghost btn-circle' }, '⚙️')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(IconsDemo, iconsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Form Field Tooltips
|
|
||||||
const formTarget = document.querySelector('#demo-form');
|
|
||||||
if (formTarget && !formTarget.hasChildNodes()) {
|
|
||||||
const FormDemo = () => {
|
|
||||||
const username = $('');
|
|
||||||
const email = $('');
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 max-w-md mx-auto' }, [
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Username',
|
|
||||||
value: username,
|
|
||||||
placeholder: 'Choose a username',
|
|
||||||
oninput: (e) => username(e.target.value)
|
|
||||||
}),
|
|
||||||
Tooltip({ tip: 'Must be at least 3 characters, letters and numbers only' }, [
|
|
||||||
Span({ class: 'cursor-help text-info' }, '?')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex items-center gap-2' }, [
|
|
||||||
Input({
|
|
||||||
label: 'Email',
|
|
||||||
type: 'email',
|
|
||||||
value: email,
|
|
||||||
placeholder: 'Enter your email',
|
|
||||||
oninput: (e) => email(e.target.value)
|
|
||||||
}),
|
|
||||||
Tooltip({ tip: 'We\'ll never share your email with anyone' }, [
|
|
||||||
Span({ class: 'cursor-help text-info' }, '?')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(FormDemo, formTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Interactive Tooltip
|
|
||||||
const interactiveTarget = document.querySelector('#demo-interactive');
|
|
||||||
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
|
|
||||||
const InteractiveDemo = () => {
|
|
||||||
const showTip = $(false);
|
|
||||||
const tooltipText = $('Hover over the button!');
|
|
||||||
|
|
||||||
const updateTooltip = (text) => {
|
|
||||||
tooltipText(text);
|
|
||||||
setTimeout(() => {
|
|
||||||
tooltipText('Hover over the button!');
|
|
||||||
}, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
return Div({ class: 'flex flex-col gap-4 items-center' }, [
|
|
||||||
Tooltip({ tip: () => tooltipText() }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-primary btn-lg',
|
|
||||||
onclick: () => Toast('Button clicked!', 'alert-info', 2000)
|
|
||||||
}, 'Interactive Button')
|
|
||||||
]),
|
|
||||||
Div({ class: 'flex gap-2 flex-wrap justify-center mt-4' }, [
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs',
|
|
||||||
onclick: () => updateTooltip('You clicked the button!')
|
|
||||||
}, 'Change Tooltip'),
|
|
||||||
Button({
|
|
||||||
class: 'btn btn-xs',
|
|
||||||
onclick: () => updateTooltip('Try hovering now!')
|
|
||||||
}, 'Change Again')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(InteractiveDemo, interactiveTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Rich Tooltip Content
|
|
||||||
const richTarget = document.querySelector('#demo-rich');
|
|
||||||
if (richTarget && !richTarget.hasChildNodes()) {
|
|
||||||
const RichDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Tooltip({
|
|
||||||
tip: Div({ class: 'text-left p-1' }, [
|
|
||||||
Div({ class: 'font-bold' }, 'User Info'),
|
|
||||||
Div({ class: 'text-xs' }, 'John Doe'),
|
|
||||||
Div({ class: 'text-xs' }, 'john@example.com'),
|
|
||||||
Div({ class: 'badge badge-xs badge-primary mt-1' }, 'Admin')
|
|
||||||
])
|
|
||||||
}, [
|
|
||||||
Button({ class: 'btn btn-outline' }, 'User Profile')
|
|
||||||
]),
|
|
||||||
Tooltip({
|
|
||||||
tip: Div({ class: 'text-left p-1' }, [
|
|
||||||
Div({ class: 'font-bold flex items-center gap-1' }, [
|
|
||||||
Icons.iconWarning,
|
|
||||||
Span({}, 'System Status')
|
|
||||||
]),
|
|
||||||
Div({ class: 'text-xs' }, 'All systems operational'),
|
|
||||||
Div({ class: 'text-xs text-success' }, '✓ 99.9% uptime')
|
|
||||||
])
|
|
||||||
}, [
|
|
||||||
Button({ class: 'btn btn-outline' }, 'System Status')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(RichDemo, richTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Color Variants
|
|
||||||
const colorsTarget = document.querySelector('#demo-colors');
|
|
||||||
if (colorsTarget && !colorsTarget.hasChildNodes()) {
|
|
||||||
const ColorsDemo = () => {
|
|
||||||
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
|
|
||||||
Tooltip({ tip: 'Primary tooltip', class: 'tooltip-primary' }, [
|
|
||||||
Button({ class: 'btn btn-primary btn-sm' }, 'Primary')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Secondary tooltip', class: 'tooltip-secondary' }, [
|
|
||||||
Button({ class: 'btn btn-secondary btn-sm' }, 'Secondary')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Accent tooltip', class: 'tooltip-accent' }, [
|
|
||||||
Button({ class: 'btn btn-accent btn-sm' }, 'Accent')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Info tooltip', class: 'tooltip-info' }, [
|
|
||||||
Button({ class: 'btn btn-info btn-sm' }, 'Info')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Success tooltip', class: 'tooltip-success' }, [
|
|
||||||
Button({ class: 'btn btn-success btn-sm' }, 'Success')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Warning tooltip', class: 'tooltip-warning' }, [
|
|
||||||
Button({ class: 'btn btn-warning btn-sm' }, 'Warning')
|
|
||||||
]),
|
|
||||||
Tooltip({ tip: 'Error tooltip', class: 'tooltip-error' }, [
|
|
||||||
Button({ class: 'btn btn-error btn-sm' }, 'Error')
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(ColorsDemo, colorsTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. All Tooltip Positions
|
|
||||||
const allPositionsTarget = document.querySelector('#demo-all-positions');
|
|
||||||
if (allPositionsTarget && !allPositionsTarget.hasChildNodes()) {
|
|
||||||
const AllPositionsDemo = () => {
|
|
||||||
return Div({ class: 'grid grid-cols-3 gap-4 justify-items-center' }, [
|
|
||||||
Div({ class: 'col-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Top tooltip', class: 'tooltip-top' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Top')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-1 row-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Left tooltip', class: 'tooltip-left' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Left')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-2 row-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Center tooltip', class: 'tooltip' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Center')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-3 row-start-2' }, [
|
|
||||||
Tooltip({ tip: 'Right tooltip', class: 'tooltip-right' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Right')
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
Div({ class: 'col-start-2 row-start-3' }, [
|
|
||||||
Tooltip({ tip: 'Bottom tooltip', class: 'tooltip-bottom' }, [
|
|
||||||
Button({ class: 'btn btn-sm w-24' }, 'Bottom')
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
$mount(AllPositionsDemo, allPositionsTarget);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
initTooltipExamples();
|
|
||||||
|
|
||||||
if (window.$docsify) {
|
|
||||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
|
||||||
hook.doneEach(initTooltipExamples);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
@@ -53,11 +53,7 @@
|
|||||||
const codeBlocks = document.querySelectorAll('pre[data-lang="javascript"] code');
|
const codeBlocks = document.querySelectorAll('pre[data-lang="javascript"] code');
|
||||||
|
|
||||||
codeBlocks.forEach(code => {
|
codeBlocks.forEach(code => {
|
||||||
// Usamos un bloque try/catch por si el código del Markdown tiene errores
|
|
||||||
try {
|
try {
|
||||||
// Ejecutamos el código.
|
|
||||||
// Como tu librería ya puso $, $mount, Button, etc. en 'window',
|
|
||||||
// el código los encontrará directamente.
|
|
||||||
const runDemo = new Function(code.innerText);
|
const runDemo = new Function(code.innerText);
|
||||||
runDemo();
|
runDemo();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ Follow these steps to integrate **SigPro-UI** into your project.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
!> **📘 Core Concepts**
|
!> **📘 Core Concepts**
|
||||||
**Note:** SigPro-UI now includes SigPro core internally. No need to install SigPro separately.
|
**Note:** SigPro-UI now includes SigPro core internally. No need to install SigPro separately.
|
||||||
SigProUI is built on top of the [SigPro](https://natxocc.github.io/sigpro/#/) reactive core. To learn how to create signals, manage reactivity, and structure your application logic, check out the [SigPro documentation](https://natxocc.github.io/sigpro/#/). It covers everything you need to build reactive applications with signals, computed values, and effects.
|
SigProUI is built on top of the [SigPro](https://natxocc.github.io/sigpro/#/) reactive core. To learn how to create signals, manage reactivity, and structure your application logic, check out the [SigPro documentation](https://natxocc.github.io/sigpro/#/). It covers everything you need to build reactive applications with signals, computed values, and effects.
|
||||||
@@ -24,15 +23,15 @@ npm install sigpro-ui
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// Import everything from sigpro-ui (includes sigpro core)
|
// Import everything from sigpro-ui (includes sigpro core)
|
||||||
import { $, $mount, Button, Alert, Input, tt } from "sigpro-ui";
|
import { $, Mount, Button, Alert, Input, tt } from "sigpro-ui";
|
||||||
import "sigpro-ui/css";
|
import "sigpro-ui/css";
|
||||||
|
|
||||||
// Create your app
|
// Create your app
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const count = $(0);
|
const count = $(0);
|
||||||
|
|
||||||
return $html('div', { class: 'p-8 max-w-md mx-auto' }, [
|
return Tag('div', { class: 'p-8 max-w-md mx-auto' }, [
|
||||||
$html('h1', { class: 'text-2xl font-bold mb-4' }, 'SigProUI Demo'),
|
Tag('h1', { class: 'text-2xl font-bold mb-4' }, 'SigProUI Demo'),
|
||||||
|
|
||||||
Input({
|
Input({
|
||||||
placeholder: 'Enter your name...'
|
placeholder: 'Enter your name...'
|
||||||
@@ -51,7 +50,7 @@ const App = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Mount your app
|
// Mount your app
|
||||||
$mount(App, "#app");
|
Mount(App, "#app");
|
||||||
```
|
```
|
||||||
|
|
||||||
### CDN Usage (no build step)
|
### CDN Usage (no build step)
|
||||||
@@ -78,14 +77,14 @@ Simply add the script tag and start using SigProUI:
|
|||||||
// All functions are available directly in window
|
// All functions are available directly in window
|
||||||
// No need to import anything!
|
// No need to import anything!
|
||||||
|
|
||||||
const { $, $mount, Button, Input, Alert } = window;
|
const { $, Mount, Button, Input, Alert } = window;
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const name = $('');
|
const name = $('');
|
||||||
const count = $(0);
|
const count = $(0);
|
||||||
|
|
||||||
return $html('div', { class: 'max-w-md mx-auto p-4' }, [
|
return Tag('div', { class: 'max-w-md mx-auto p-4' }, [
|
||||||
$html('h1', { class: 'text-2xl font-bold mb-4' }, 'SigProUI Demo'),
|
Tag('h1', { class: 'text-2xl font-bold mb-4' }, 'SigProUI Demo'),
|
||||||
|
|
||||||
Input({
|
Input({
|
||||||
value: name,
|
value: name,
|
||||||
@@ -104,7 +103,7 @@ Simply add the script tag and start using SigProUI:
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
$mount(App, '#app');
|
Mount(App, '#app');
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -116,12 +115,12 @@ When you install SigProUI, you get:
|
|||||||
|
|
||||||
### SigPro Core Functions
|
### SigPro Core Functions
|
||||||
- `$()` - Reactive signals
|
- `$()` - Reactive signals
|
||||||
- `$watch()` - Watch reactive dependencies
|
- `Watch()` - Watch reactive dependencies
|
||||||
- `$html()` - Create HTML elements with reactivity
|
- `Tag()` - Create HTML elements with reactivity
|
||||||
- `$if()` - Conditional rendering
|
- `If()` - Conditional rendering
|
||||||
- `$for()` - List rendering
|
- `For()` - List rendering
|
||||||
- `$router()` - Hash-based routing
|
- `Router()` - Hash-based routing
|
||||||
- `$mount()` - Mount components to DOM
|
- `Mount()` - Mount components to DOM
|
||||||
|
|
||||||
>For more information about SigPro Core visit official Docs [SigPro Docs](https://natxocc.github.io/sigpro/#/)
|
>For more information about SigPro Core visit official Docs [SigPro Docs](https://natxocc.github.io/sigpro/#/)
|
||||||
|
|
||||||
|
|||||||
@@ -184,14 +184,14 @@ Input({
|
|||||||
const userId = $("123");
|
const userId = $("123");
|
||||||
const userData = $(null);
|
const userData = $(null);
|
||||||
|
|
||||||
$watch(userId, async (id) => {
|
Watch(userId, async (id) => {
|
||||||
loading(true);
|
loading(true);
|
||||||
userData(await fetch(`/api/user/${id}`).then(r => r.json()));
|
userData(await fetch(`/api/user/${id}`).then(r => r.json()));
|
||||||
loading(false);
|
loading(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// In template
|
// In template
|
||||||
$if(() => userData(), () => Alert({ type: "success" }, userData()?.name))
|
If(() => userData(), () => Alert({ type: "success" }, userData()?.name))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Modal with Confirm Action
|
### Modal with Confirm Action
|
||||||
@@ -222,6 +222,6 @@ Modal({
|
|||||||
| `Alert` | `type` (info/success/warning/error), `soft`, `actions` |
|
| `Alert` | `type` (info/success/warning/error), `soft`, `actions` |
|
||||||
| `Toast` | `message`, `type`, `duration` |
|
| `Toast` | `message`, `type`, `duration` |
|
||||||
| `Datepicker` | `value`, `range`, `label`, `placeholder` |
|
| `Datepicker` | `value`, `range`, `label`, `placeholder` |
|
||||||
| `Autocomplete` | `options`, `value`, `onSelect`, `label` |
|
| `Autocomplete` | `options`, `value`, `onselect`, `label` |
|
||||||
| `Indicator` | `value` (function that returns number/string) |
|
| `Indicator` | `value` (function that returns number/string) |
|
||||||
| `Tooltip` | `tip`, `ui` (tooltip-top/bottom/left/right) |
|
| `Tooltip` | `tip`, `ui` (tooltip-top/bottom/left/right) |
|
||||||
|
|||||||
2053
docs/sigpro-ui.min.js
vendored
2053
docs/sigpro-ui.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
498
index.d.ts
vendored
Normal file
498
index.d.ts
vendored
Normal file
@@ -0,0 +1,498 @@
|
|||||||
|
// sigpro-ui.d.ts
|
||||||
|
|
||||||
|
declare module 'sigpro-ui' {
|
||||||
|
// Tipos básicos
|
||||||
|
type Signal<T> = {
|
||||||
|
(): T;
|
||||||
|
(value: T | ((prev: T) => T)): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ComponentFunction<P = {}> = (props?: P, children?: any) => HTMLElement | string | null;
|
||||||
|
type ComponentChild = HTMLElement | string | number | boolean | null | undefined;
|
||||||
|
type ComponentChildren = ComponentChild | ComponentChild[];
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
function val<T>(value: T | (() => T)): T;
|
||||||
|
function ui(baseClass: string, ...additional: (string | (() => string) | undefined)[]): string | (() => string);
|
||||||
|
function getIcon(icon: string | (() => string) | HTMLElement | null | undefined): HTMLElement | null;
|
||||||
|
function tt(key: 'close' | 'confirm' | 'cancel' | 'search' | 'loading' | 'nodata'): () => string;
|
||||||
|
|
||||||
|
// Props comunes
|
||||||
|
interface BaseProps {
|
||||||
|
class?: string | (() => string);
|
||||||
|
style?: string | Record<string, string> | (() => string | Record<string, string>);
|
||||||
|
id?: string | (() => string);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventProps {
|
||||||
|
onclick?: (event: MouseEvent) => void;
|
||||||
|
oninput?: (event: Event) => void;
|
||||||
|
onchange?: (event: Event) => void;
|
||||||
|
onblur?: (event: FocusEvent) => void;
|
||||||
|
onfocus?: (event: FocusEvent) => void;
|
||||||
|
onkeydown?: (event: KeyboardEvent) => void;
|
||||||
|
onkeyup?: (event: KeyboardEvent) => void;
|
||||||
|
onmouseenter?: (event: MouseEvent) => void;
|
||||||
|
onmouseleave?: (event: MouseEvent) => void;
|
||||||
|
onsubmit?: (event: Event) => void;
|
||||||
|
ondragover?: (event: DragEvent) => void;
|
||||||
|
ondragleave?: (event: DragEvent) => void;
|
||||||
|
ondrop?: (event: DragEvent) => void;
|
||||||
|
[key: `on${string}`]: ((event: any) => void) | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accordion
|
||||||
|
interface AccordionProps extends BaseProps, EventProps {
|
||||||
|
title: string | (() => string);
|
||||||
|
name?: string;
|
||||||
|
open?: boolean | (() => boolean);
|
||||||
|
}
|
||||||
|
function Accordion(props: AccordionProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Alert
|
||||||
|
type AlertType = 'info' | 'success' | 'warning' | 'error';
|
||||||
|
interface AlertProps extends BaseProps, EventProps {
|
||||||
|
type?: AlertType;
|
||||||
|
soft?: boolean;
|
||||||
|
actions?: ComponentFunction | ComponentChildren;
|
||||||
|
message?: string | (() => string);
|
||||||
|
}
|
||||||
|
function Alert(props: AlertProps, children?: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Autocomplete
|
||||||
|
interface AutocompleteOption {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
interface AutocompleteProps extends BaseProps, EventProps {
|
||||||
|
items?: string[] | AutocompleteOption[] | (() => (string[] | AutocompleteOption[]));
|
||||||
|
value?: Signal<string> | string;
|
||||||
|
onselect?: (option: string | AutocompleteOption) => void;
|
||||||
|
label?: string | (() => string);
|
||||||
|
placeholder?: string | (() => string);
|
||||||
|
}
|
||||||
|
function Autocomplete(props: AutocompleteProps): HTMLElement;
|
||||||
|
|
||||||
|
// Badge
|
||||||
|
interface BadgeProps extends BaseProps, EventProps {}
|
||||||
|
function Badge(props: BadgeProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Button
|
||||||
|
interface ButtonProps extends BaseProps, EventProps {
|
||||||
|
disabled?: boolean | (() => boolean);
|
||||||
|
loading?: boolean | (() => boolean);
|
||||||
|
icon?: string | (() => string) | HTMLElement;
|
||||||
|
}
|
||||||
|
function Button(props: ButtonProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Checkbox
|
||||||
|
interface CheckboxProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<boolean> | boolean;
|
||||||
|
label?: string | (() => string);
|
||||||
|
tooltip?: string | (() => string);
|
||||||
|
toggle?: boolean | (() => boolean);
|
||||||
|
}
|
||||||
|
function Checkbox(props: CheckboxProps): HTMLElement;
|
||||||
|
|
||||||
|
// Colorpicker
|
||||||
|
interface ColorpickerProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<string> | string;
|
||||||
|
label?: string | (() => string);
|
||||||
|
}
|
||||||
|
function Colorpicker(props: ColorpickerProps): HTMLElement;
|
||||||
|
|
||||||
|
// Datepicker
|
||||||
|
interface DateRange {
|
||||||
|
start: string;
|
||||||
|
end: string | null;
|
||||||
|
startHour?: number;
|
||||||
|
endHour?: number;
|
||||||
|
}
|
||||||
|
interface DatepickerProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<string | DateRange> | string | DateRange;
|
||||||
|
range?: boolean | (() => boolean);
|
||||||
|
label?: string | (() => string);
|
||||||
|
placeholder?: string | (() => string);
|
||||||
|
hour?: boolean;
|
||||||
|
}
|
||||||
|
function Datepicker(props: DatepickerProps): HTMLElement;
|
||||||
|
|
||||||
|
// Drawer
|
||||||
|
interface DrawerProps extends BaseProps, EventProps {
|
||||||
|
id?: string;
|
||||||
|
open?: Signal<boolean> | boolean;
|
||||||
|
side: ComponentFunction | ComponentChildren;
|
||||||
|
content: ComponentFunction | ComponentChildren;
|
||||||
|
}
|
||||||
|
function Drawer(props: DrawerProps, children?: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Dropdown
|
||||||
|
interface DropdownItem {
|
||||||
|
label: string | (() => string);
|
||||||
|
icon?: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
onclick?: (event: MouseEvent) => void;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
interface DropdownProps extends BaseProps, EventProps {
|
||||||
|
label?: string | (() => string);
|
||||||
|
icon?: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
items?: DropdownItem[] | (() => DropdownItem[]);
|
||||||
|
}
|
||||||
|
function Dropdown(props: DropdownProps): HTMLElement;
|
||||||
|
|
||||||
|
// Fab
|
||||||
|
interface FabAction {
|
||||||
|
label?: string;
|
||||||
|
icon?: string | HTMLElement;
|
||||||
|
text?: string;
|
||||||
|
onclick?: (event: MouseEvent) => void;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
interface FabProps extends BaseProps, EventProps {
|
||||||
|
icon?: string | HTMLElement;
|
||||||
|
label?: string;
|
||||||
|
actions?: FabAction[] | (() => FabAction[]);
|
||||||
|
position?: string;
|
||||||
|
}
|
||||||
|
function Fab(props: FabProps): HTMLElement;
|
||||||
|
|
||||||
|
// Fieldset
|
||||||
|
interface FieldsetProps extends BaseProps, EventProps {
|
||||||
|
legend?: string | (() => string);
|
||||||
|
}
|
||||||
|
function Fieldset(props: FieldsetProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Fileinput
|
||||||
|
interface FileinputProps extends BaseProps, EventProps {
|
||||||
|
tooltip?: string;
|
||||||
|
max?: number;
|
||||||
|
accept?: string;
|
||||||
|
onselect?: (files: File[]) => void;
|
||||||
|
}
|
||||||
|
function Fileinput(props: FileinputProps): HTMLElement;
|
||||||
|
|
||||||
|
// Indicator
|
||||||
|
interface IndicatorProps extends BaseProps, EventProps {
|
||||||
|
value?: number | string | (() => number | string);
|
||||||
|
}
|
||||||
|
function Indicator(props: IndicatorProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Input
|
||||||
|
interface InputProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<string> | string;
|
||||||
|
type?: string;
|
||||||
|
icon?: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
placeholder?: string | (() => string);
|
||||||
|
disabled?: boolean | (() => boolean);
|
||||||
|
size?: string;
|
||||||
|
validate?: (value: string) => string | null | undefined;
|
||||||
|
label?: string | (() => string);
|
||||||
|
}
|
||||||
|
function Input(props: InputProps): HTMLElement;
|
||||||
|
|
||||||
|
// Label
|
||||||
|
interface LabelProps extends BaseProps, EventProps {
|
||||||
|
value?: string | (() => string);
|
||||||
|
floating?: boolean;
|
||||||
|
error?: string | (() => string);
|
||||||
|
required?: boolean;
|
||||||
|
}
|
||||||
|
function Label(props: LabelProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// List
|
||||||
|
interface ListProps<T = any> extends BaseProps, EventProps {
|
||||||
|
items: T[] | (() => T[]);
|
||||||
|
header?: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
render: (item: T, index: number) => ComponentChild;
|
||||||
|
keyFn?: (item: T, index: number) => string | number;
|
||||||
|
}
|
||||||
|
function List<T>(props: ListProps<T>): HTMLElement;
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
interface MenuItem {
|
||||||
|
label: string | (() => string);
|
||||||
|
icon?: string | HTMLElement;
|
||||||
|
onclick?: (event: MouseEvent) => void;
|
||||||
|
active?: boolean | (() => boolean);
|
||||||
|
children?: MenuItem[];
|
||||||
|
open?: boolean;
|
||||||
|
}
|
||||||
|
interface MenuProps extends BaseProps, EventProps {
|
||||||
|
items: MenuItem[] | (() => MenuItem[]);
|
||||||
|
}
|
||||||
|
function Menu(props: MenuProps): HTMLElement;
|
||||||
|
|
||||||
|
// Modal
|
||||||
|
interface ModalProps extends BaseProps, EventProps {
|
||||||
|
open?: Signal<boolean> | boolean;
|
||||||
|
title?: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
buttons?: ComponentFunction | ComponentFunction[];
|
||||||
|
}
|
||||||
|
function Modal(props: ModalProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Navbar
|
||||||
|
interface NavbarProps extends BaseProps, EventProps {}
|
||||||
|
function Navbar(props: NavbarProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Radio
|
||||||
|
interface RadioProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<any> | any;
|
||||||
|
inputValue?: any;
|
||||||
|
name?: string;
|
||||||
|
label?: string | (() => string);
|
||||||
|
tooltip?: string | (() => string);
|
||||||
|
}
|
||||||
|
function Radio(props: RadioProps): HTMLElement;
|
||||||
|
|
||||||
|
// Range
|
||||||
|
interface RangeProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<number> | number;
|
||||||
|
label?: string | (() => string);
|
||||||
|
tooltip?: string | (() => string);
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
step?: number;
|
||||||
|
disabled?: boolean | (() => boolean);
|
||||||
|
}
|
||||||
|
function Range(props: RangeProps): HTMLElement;
|
||||||
|
|
||||||
|
// Rating
|
||||||
|
interface RatingProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<number> | number;
|
||||||
|
count?: number | (() => number);
|
||||||
|
mask?: string;
|
||||||
|
readonly?: boolean | (() => boolean);
|
||||||
|
onchange?: (value: number) => void;
|
||||||
|
}
|
||||||
|
function Rating(props: RatingProps): HTMLElement;
|
||||||
|
|
||||||
|
// Select
|
||||||
|
interface SelectOption {
|
||||||
|
label: string;
|
||||||
|
value: string | number;
|
||||||
|
}
|
||||||
|
interface SelectProps extends BaseProps, EventProps {
|
||||||
|
label?: string | (() => string);
|
||||||
|
items?: SelectOption[] | (() => SelectOption[]);
|
||||||
|
value?: Signal<string | number> | string | number;
|
||||||
|
}
|
||||||
|
function Select(props: SelectProps): HTMLElement;
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
interface StackProps extends BaseProps, EventProps {}
|
||||||
|
function Stack(props: StackProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Stat
|
||||||
|
interface StatProps extends BaseProps, EventProps {
|
||||||
|
icon?: string | HTMLElement;
|
||||||
|
label?: string | (() => string);
|
||||||
|
value?: string | number | (() => string | number);
|
||||||
|
desc?: string | (() => string);
|
||||||
|
}
|
||||||
|
function Stat(props: StatProps): HTMLElement;
|
||||||
|
|
||||||
|
// Swap
|
||||||
|
interface SwapProps extends BaseProps, EventProps {
|
||||||
|
value?: Signal<boolean> | boolean;
|
||||||
|
on: ComponentChildren;
|
||||||
|
off: ComponentChildren;
|
||||||
|
}
|
||||||
|
function Swap(props: SwapProps): HTMLElement;
|
||||||
|
|
||||||
|
// Table
|
||||||
|
interface TableColumn<T = any> {
|
||||||
|
label: string | (() => string);
|
||||||
|
key?: keyof T;
|
||||||
|
render?: (item: T, index: number) => ComponentChild;
|
||||||
|
class?: string;
|
||||||
|
}
|
||||||
|
interface TableProps<T = any> extends BaseProps, EventProps {
|
||||||
|
items: T[] | (() => T[]);
|
||||||
|
columns: TableColumn<T>[];
|
||||||
|
keyFn?: (item: T, index: number) => string | number;
|
||||||
|
zebra?: boolean | (() => boolean);
|
||||||
|
pinRows?: boolean | (() => boolean);
|
||||||
|
empty?: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
}
|
||||||
|
function Table<T>(props: TableProps<T>): HTMLElement;
|
||||||
|
|
||||||
|
// Tabs
|
||||||
|
interface TabItem {
|
||||||
|
label: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
content: ComponentFunction | ComponentChildren;
|
||||||
|
active?: boolean | (() => boolean);
|
||||||
|
disabled?: boolean | (() => boolean);
|
||||||
|
onclick?: () => void;
|
||||||
|
}
|
||||||
|
interface TabsProps extends BaseProps, EventProps {
|
||||||
|
items: TabItem[] | (() => TabItem[]);
|
||||||
|
}
|
||||||
|
function Tabs(props: TabsProps): HTMLElement;
|
||||||
|
|
||||||
|
// Timeline
|
||||||
|
interface TimelineItem {
|
||||||
|
title: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
detail: string | HTMLElement | (() => string | HTMLElement);
|
||||||
|
type?: 'info' | 'success' | 'warning' | 'error';
|
||||||
|
icon?: string | HTMLElement;
|
||||||
|
completed?: boolean | (() => boolean);
|
||||||
|
}
|
||||||
|
interface TimelineProps extends BaseProps, EventProps {
|
||||||
|
items: TimelineItem[] | (() => TimelineItem[]);
|
||||||
|
vertical?: boolean | (() => boolean);
|
||||||
|
compact?: boolean | (() => boolean);
|
||||||
|
}
|
||||||
|
function Timeline(props: TimelineProps): HTMLElement;
|
||||||
|
|
||||||
|
// Toast
|
||||||
|
type ToastType = 'alert-info' | 'alert-success' | 'alert-warning' | 'alert-error';
|
||||||
|
function Toast(
|
||||||
|
message: string | (() => string),
|
||||||
|
type?: ToastType,
|
||||||
|
duration?: number
|
||||||
|
): () => void;
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
interface TooltipProps extends BaseProps, EventProps {
|
||||||
|
tip: string | (() => string);
|
||||||
|
ui?: string;
|
||||||
|
}
|
||||||
|
function Tooltip(props: TooltipProps, children: ComponentChildren): HTMLElement;
|
||||||
|
|
||||||
|
// Objeto principal
|
||||||
|
const SigProUI: {
|
||||||
|
Accordion: typeof Accordion;
|
||||||
|
Alert: typeof Alert;
|
||||||
|
Autocomplete: typeof Autocomplete;
|
||||||
|
Badge: typeof Badge;
|
||||||
|
Button: typeof Button;
|
||||||
|
Checkbox: typeof Checkbox;
|
||||||
|
Colorpicker: typeof Colorpicker;
|
||||||
|
Datepicker: typeof Datepicker;
|
||||||
|
Drawer: typeof Drawer;
|
||||||
|
Dropdown: typeof Dropdown;
|
||||||
|
Fab: typeof Fab;
|
||||||
|
Fieldset: typeof Fieldset;
|
||||||
|
Fileinput: typeof Fileinput;
|
||||||
|
Indicator: typeof Indicator;
|
||||||
|
Input: typeof Input;
|
||||||
|
Label: typeof Label;
|
||||||
|
List: typeof List;
|
||||||
|
Menu: typeof Menu;
|
||||||
|
Modal: typeof Modal;
|
||||||
|
Navbar: typeof Navbar;
|
||||||
|
Radio: typeof Radio;
|
||||||
|
Range: typeof Range;
|
||||||
|
Rating: typeof Rating;
|
||||||
|
Select: typeof Select;
|
||||||
|
Stack: typeof Stack;
|
||||||
|
Stat: typeof Stat;
|
||||||
|
Swap: typeof Swap;
|
||||||
|
Table: typeof Table;
|
||||||
|
Tabs: typeof Tabs;
|
||||||
|
Timeline: typeof Timeline;
|
||||||
|
Toast: typeof Toast;
|
||||||
|
Tooltip: typeof Tooltip;
|
||||||
|
Utils: {
|
||||||
|
val: typeof val;
|
||||||
|
ui: typeof ui;
|
||||||
|
getIcon: typeof getIcon;
|
||||||
|
};
|
||||||
|
tt: typeof tt;
|
||||||
|
install: (target?: Window) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SigProUI;
|
||||||
|
export {
|
||||||
|
Accordion, Alert, Autocomplete, Badge, Button, Checkbox, Colorpicker,
|
||||||
|
Datepicker, Drawer, Dropdown, Fab, Fieldset, Fileinput, Indicator,
|
||||||
|
Input, Label, List, Menu, Modal, Navbar, Radio, Range, Rating,
|
||||||
|
Select, Stack, Stat, Swap, Table, Tabs, Timeline, Toast, Tooltip,
|
||||||
|
val, ui, getIcon, tt,
|
||||||
|
// Tipos
|
||||||
|
AccordionProps, AlertProps, AutocompleteProps, BadgeProps, ButtonProps,
|
||||||
|
CheckboxProps, ColorpickerProps, DatepickerProps, DrawerProps, DropdownProps,
|
||||||
|
FabProps, FieldsetProps, FileinputProps, IndicatorProps, InputProps,
|
||||||
|
LabelProps, ListProps, MenuProps, ModalProps, NavbarProps, RadioProps,
|
||||||
|
RangeProps, RatingProps, SelectProps, StackProps, StatProps, SwapProps,
|
||||||
|
TableProps, TabsProps, TimelineProps, TooltipProps,
|
||||||
|
DateRange, AutocompleteOption, DropdownItem, FabAction, MenuItem,
|
||||||
|
SelectOption, TabItem, TableColumn, TimelineItem, ToastType, AlertType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declaraciones globales
|
||||||
|
declare global {
|
||||||
|
const Accordion: typeof import('sigpro-ui').Accordion;
|
||||||
|
const Alert: typeof import('sigpro-ui').Alert;
|
||||||
|
const Autocomplete: typeof import('sigpro-ui').Autocomplete;
|
||||||
|
const Badge: typeof import('sigpro-ui').Badge;
|
||||||
|
const Button: typeof import('sigpro-ui').Button;
|
||||||
|
const Checkbox: typeof import('sigpro-ui').Checkbox;
|
||||||
|
const Colorpicker: typeof import('sigpro-ui').Colorpicker;
|
||||||
|
const Datepicker: typeof import('sigpro-ui').Datepicker;
|
||||||
|
const Drawer: typeof import('sigpro-ui').Drawer;
|
||||||
|
const Dropdown: typeof import('sigpro-ui').Dropdown;
|
||||||
|
const Fab: typeof import('sigpro-ui').Fab;
|
||||||
|
const Fieldset: typeof import('sigpro-ui').Fieldset;
|
||||||
|
const Fileinput: typeof import('sigpro-ui').Fileinput;
|
||||||
|
const Indicator: typeof import('sigpro-ui').Indicator;
|
||||||
|
const Input: typeof import('sigpro-ui').Input;
|
||||||
|
const Label: typeof import('sigpro-ui').Label;
|
||||||
|
const List: typeof import('sigpro-ui').List;
|
||||||
|
const Menu: typeof import('sigpro-ui').Menu;
|
||||||
|
const Modal: typeof import('sigpro-ui').Modal;
|
||||||
|
const Navbar: typeof import('sigpro-ui').Navbar;
|
||||||
|
const Radio: typeof import('sigpro-ui').Radio;
|
||||||
|
const Range: typeof import('sigpro-ui').Range;
|
||||||
|
const Rating: typeof import('sigpro-ui').Rating;
|
||||||
|
const Select: typeof import('sigpro-ui').Select;
|
||||||
|
const Stack: typeof import('sigpro-ui').Stack;
|
||||||
|
const Stat: typeof import('sigpro-ui').Stat;
|
||||||
|
const Swap: typeof import('sigpro-ui').Swap;
|
||||||
|
const Table: typeof import('sigpro-ui').Table;
|
||||||
|
const Tabs: typeof import('sigpro-ui').Tabs;
|
||||||
|
const Timeline: typeof import('sigpro-ui').Timeline;
|
||||||
|
const Toast: typeof import('sigpro-ui').Toast;
|
||||||
|
const Tooltip: typeof import('sigpro-ui').Tooltip;
|
||||||
|
const Utils: typeof import('sigpro-ui').Utils;
|
||||||
|
const tt: typeof import('sigpro-ui').tt;
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
Accordion: typeof Accordion;
|
||||||
|
Alert: typeof Alert;
|
||||||
|
Autocomplete: typeof Autocomplete;
|
||||||
|
Badge: typeof Badge;
|
||||||
|
Button: typeof Button;
|
||||||
|
Checkbox: typeof Checkbox;
|
||||||
|
Colorpicker: typeof Colorpicker;
|
||||||
|
Datepicker: typeof Datepicker;
|
||||||
|
Drawer: typeof Drawer;
|
||||||
|
Dropdown: typeof Dropdown;
|
||||||
|
Fab: typeof Fab;
|
||||||
|
Fieldset: typeof Fieldset;
|
||||||
|
Fileinput: typeof Fileinput;
|
||||||
|
Indicator: typeof Indicator;
|
||||||
|
Input: typeof Input;
|
||||||
|
Label: typeof Label;
|
||||||
|
List: typeof List;
|
||||||
|
Menu: typeof Menu;
|
||||||
|
Modal: typeof Modal;
|
||||||
|
Navbar: typeof Navbar;
|
||||||
|
Radio: typeof Radio;
|
||||||
|
Range: typeof Range;
|
||||||
|
Rating: typeof Rating;
|
||||||
|
Select: typeof Select;
|
||||||
|
Stack: typeof Stack;
|
||||||
|
Stat: typeof Stat;
|
||||||
|
Swap: typeof Swap;
|
||||||
|
Table: typeof Table;
|
||||||
|
Tabs: typeof Tabs;
|
||||||
|
Timeline: typeof Timeline;
|
||||||
|
Toast: typeof Toast;
|
||||||
|
Tooltip: typeof Tooltip;
|
||||||
|
Utils: typeof Utils;
|
||||||
|
tt: typeof tt;
|
||||||
|
SigProUI: typeof import('sigpro-ui').default;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
index.js
53
index.js
@@ -1,25 +1,56 @@
|
|||||||
// index.js
|
// import './src/sigpro.js';
|
||||||
import './src/sigpro.js';
|
import { $, Watch, Tag, If, For, Router, Mount } from './src/sigpro.js';
|
||||||
// import './src/css/sigpro.css'; // No importes CSS en JS
|
|
||||||
import * as Components from './src/components/index.js';
|
import * as Components from './src/components/index.js';
|
||||||
// import * as Icons from './src/core/icons.js'; // ELIMINAR
|
|
||||||
import * as Utils from './src/core/utils.js';
|
import * as Utils from './src/core/utils.js';
|
||||||
import { tt } from './src/core/i18n.js';
|
import { tt } from './src/core/i18n.js';
|
||||||
|
|
||||||
export * from './src/components/index.js';
|
export * from './src/components/index.js';
|
||||||
// export * from './src/core/icons.js'; // ELIMINAR
|
|
||||||
export * from './src/core/utils.js';
|
export * from './src/core/utils.js';
|
||||||
export { tt };
|
export { $, Watch, Tag, If, For, Router, Mount, tt };
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
|
// const CoreAPI = { $, $$, ender, Watch, Tag, If, For, Router, Mount } = SigPro;
|
||||||
|
|
||||||
|
// Object.entries(CoreAPI).forEach(([name, fn]) => {
|
||||||
|
// Object.defineProperty(window, name, {
|
||||||
|
// value: fn,
|
||||||
|
// writable: false,
|
||||||
|
// configurable: false,
|
||||||
|
// enumerable: true
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
Object.entries(Components).forEach(([name, component]) => {
|
Object.entries(Components).forEach(([name, component]) => {
|
||||||
window[name] = component;
|
Object.defineProperty(window, name, {
|
||||||
|
value: component,
|
||||||
|
writable: false,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// window.Icons = Icons; // ELIMINAR
|
Object.entries(Utils).forEach(([name, fn]) => {
|
||||||
window.Utils = Utils;
|
Object.defineProperty(window, name, {
|
||||||
window.tt = tt;
|
value: fn,
|
||||||
window.SigProUI = { ...Components, Utils, tt };
|
writable: false,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'tt', {
|
||||||
|
value: tt,
|
||||||
|
writable: false,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'SigProUI', {
|
||||||
|
value: { ...Components, Utils, tt },
|
||||||
|
writable: false,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
|
||||||
console.log("🎨 SigProUI ready");
|
console.log("🎨 SigProUI ready");
|
||||||
}
|
}
|
||||||
27
package.json
27
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sigpro-ui",
|
"name": "sigpro-ui",
|
||||||
"version": "1.1.1",
|
"version": "1.1.4",
|
||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"module": "./index.js",
|
"module": "./index.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -12,8 +12,9 @@
|
|||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"import": "./index.js",
|
"import": "./dist/sigpro-ui.esm.js",
|
||||||
"script": "./dist/sigpro-ui.js"
|
"script": "./dist/sigpro-ui.js",
|
||||||
|
"types": "./index.d.ts"
|
||||||
},
|
},
|
||||||
"./css": {
|
"./css": {
|
||||||
"import": "./css/index.js",
|
"import": "./css/index.js",
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
"index.d.ts",
|
||||||
"dist",
|
"dist",
|
||||||
"css",
|
"css",
|
||||||
"README.md",
|
"README.md",
|
||||||
@@ -37,13 +39,20 @@
|
|||||||
],
|
],
|
||||||
"jsdelivr": "./dist/sigpro-ui.min.js",
|
"jsdelivr": "./dist/sigpro-ui.min.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"sideEffects": [
|
||||||
|
"./css/*",
|
||||||
|
"**/*.css"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:cssmin": "./node_modules/.bin/tailwindcss -i ./src/css/sigpro.css -o ./css/sigpro.min.css --content './src/**/*.js' --minify",
|
"clean": "rm -rf ./dist ./css/*.css ./docs/*.js ./docs/*.css",
|
||||||
"build:css": "./node_modules/.bin/tailwindcss -i ./src/css/sigpro.css -o ./css/sigpro.css --content './src/**/*.js'",
|
"build:cssmin": "tailwindcss -i ./src/css/sigpro.css -o ./css/sigpro.min.css --content './src/**/*.js' --minify",
|
||||||
"build:cssdocs": "./node_modules/.bin/tailwindcss -i ./src/css/sigpro.css -o ./docs/sigpro.css --content './src/**/*.js' --minify",
|
"build:css": "tailwindcss -i ./src/css/sigpro.css -o ./css/sigpro.css --content './src/**/*.js'",
|
||||||
"build:js": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.js --format=iife --global-name=SigProUI && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify",
|
"build:cssdocs": "tailwindcss -i ./src/css/sigpro.css -o ./docs/sigpro.css --content './src/**/*.js' --minify",
|
||||||
"build:jsdocs": "bun build ./index.js --bundle --outfile=./docs/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify",
|
"build:js": "bun run build:js:iife && bun run build:js:esm",
|
||||||
"build": "bun run build:css && bun run build:js && bun run build:jsdocs && bun run build:cssdocs && bun run build:cssmin",
|
"build:js:iife": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.js --format=iife --global-name=SigProUI && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.min.js --format=iife --global-name=SigProUI --minify",
|
||||||
|
"build:js:esm": "bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.js --format=esm && bun build ./index.js --bundle --outfile=./dist/sigpro-ui.esm.min.js --format=esm --minify",
|
||||||
|
"build:jsdocs": "bun build ./index.js --bundle --outfile=./docs/sigpro-ui.min.js --format=iife --global-name=SigProUI ",
|
||||||
|
"build": "bun run clean && bun run build:css && bun run build:js && bun run build:jsdocs && bun run build:cssdocs && bun run build:cssmin",
|
||||||
"prepublishOnly": "bun run build",
|
"prepublishOnly": "bun run build",
|
||||||
"docs": "bun x serve docs"
|
"docs": "bun x serve docs"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Accordion.js
|
// components/Accordion.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { ui, val } from "../core/utils.js";
|
import { ui, val } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,16 +14,16 @@ import { ui, val } from "../core/utils.js";
|
|||||||
export const Accordion = (props, children) => {
|
export const Accordion = (props, children) => {
|
||||||
const { class: className, title, name, open, ...rest } = props;
|
const { class: className, title, name, open, ...rest } = props;
|
||||||
|
|
||||||
return $html("div", {
|
return Tag("div", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui('collapse collapse-arrow bg-base-200 mb-2', className),
|
class: ui('collapse collapse-arrow bg-base-200 mb-2', className),
|
||||||
}, [
|
}, [
|
||||||
$html("input", {
|
Tag("input", {
|
||||||
type: name ? "radio" : "checkbox",
|
type: name ? "radio" : "checkbox",
|
||||||
name: name,
|
name: name,
|
||||||
checked: val(open),
|
checked: val(open),
|
||||||
}),
|
}),
|
||||||
$html("div", { class: "collapse-title text-xl font-medium" }, title),
|
Tag("div", { class: "collapse-title text-xl font-medium" }, title),
|
||||||
$html("div", { class: "collapse-content" }, children),
|
Tag("div", { class: "collapse-content" }, children),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Alert.js
|
// components/Alert.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { ui, getIcon } from "../core/utils.js";
|
import { ui, getIcon } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,16 +26,16 @@ export const Alert = (props, children) => {
|
|||||||
|
|
||||||
const content = children || props.message;
|
const content = children || props.message;
|
||||||
|
|
||||||
return $html("div", {
|
return Tag("div", {
|
||||||
...rest,
|
...rest,
|
||||||
role: "alert",
|
role: "alert",
|
||||||
class: ui('alert', allClasses),
|
class: ui('alert', allClasses),
|
||||||
}, () => [
|
}, () => [
|
||||||
getIcon(iconMap[type]),
|
getIcon(iconMap[type]),
|
||||||
$html("div", { class: "flex-1" }, [
|
Tag("div", { class: "flex-1" }, [
|
||||||
$html("span", {}, [typeof content === "function" ? content() : content])
|
Tag("span", {}, [typeof content === "function" ? content() : content])
|
||||||
]),
|
]),
|
||||||
actions ? $html("div", { class: "flex-none" }, [
|
actions ? Tag("div", { class: "flex-none" }, [
|
||||||
typeof actions === "function" ? actions() : actions
|
typeof actions === "function" ? actions() : actions
|
||||||
]) : null,
|
]) : null,
|
||||||
].filter(Boolean));
|
].filter(Boolean));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Autocomplete.js
|
// components/Autocomplete.js
|
||||||
import { $, $html, $for } from "../sigpro.js";
|
// import { $, Tag, For } from "../sigpro.js";
|
||||||
import { val } from "../core/utils.js";
|
import { val } from "../core/utils.js";
|
||||||
import { tt } from "../core/i18n.js";
|
import { tt } from "../core/i18n.js";
|
||||||
import { Input } from "./Input.js";
|
import { Input } from "./Input.js";
|
||||||
@@ -15,7 +15,7 @@ import { Input } from "./Input.js";
|
|||||||
* - z-50, active, bg-primary, text-primary-content
|
* - z-50, active, bg-primary, text-primary-content
|
||||||
*/
|
*/
|
||||||
export const Autocomplete = (props) => {
|
export const Autocomplete = (props) => {
|
||||||
const { class: className, items = [], value, onSelect, label, placeholder, ...rest } = props;
|
const { class: className, items = [], value, onselect, label, placeholder, ...rest } = props;
|
||||||
|
|
||||||
const query = $(val(value) || "");
|
const query = $(val(value) || "");
|
||||||
const isOpen = $(false);
|
const isOpen = $(false);
|
||||||
@@ -35,7 +35,7 @@ export const Autocomplete = (props) => {
|
|||||||
|
|
||||||
query(labelStr);
|
query(labelStr);
|
||||||
if (typeof value === "function") value(valStr);
|
if (typeof value === "function") value(valStr);
|
||||||
onSelect?.(opt);
|
onselect?.(opt);
|
||||||
|
|
||||||
isOpen(false);
|
isOpen(false);
|
||||||
cursor(-1);
|
cursor(-1);
|
||||||
@@ -58,7 +58,7 @@ export const Autocomplete = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return $html("div", { class: 'relative w-full' }, [
|
return Tag("div", { class: 'relative w-full' }, [
|
||||||
Input({
|
Input({
|
||||||
label,
|
label,
|
||||||
class: className,
|
class: className,
|
||||||
@@ -76,18 +76,18 @@ export const Autocomplete = (props) => {
|
|||||||
},
|
},
|
||||||
...rest,
|
...rest,
|
||||||
}),
|
}),
|
||||||
$html(
|
Tag(
|
||||||
"ul",
|
"ul",
|
||||||
{
|
{
|
||||||
class: "absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
class: "absolute dropdown-menu left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",
|
||||||
style: () => (isOpen() && list().length ? "display:block" : "display:none"),
|
style: () => (isOpen() && list().length ? "display:block" : "display:none"),
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
$for(
|
For(
|
||||||
list,
|
list,
|
||||||
(opt, i) =>
|
(opt, i) =>
|
||||||
$html("li", {}, [
|
Tag("li", {}, [
|
||||||
$html(
|
Tag(
|
||||||
"a",
|
"a",
|
||||||
{
|
{
|
||||||
class: () => `block w-full ${cursor() === i ? "active bg-primary text-primary-content" : ""}`,
|
class: () => `block w-full ${cursor() === i ? "active bg-primary text-primary-content" : ""}`,
|
||||||
@@ -99,7 +99,7 @@ export const Autocomplete = (props) => {
|
|||||||
]),
|
]),
|
||||||
(opt, i) => (typeof opt === "string" ? opt : opt.value) + i,
|
(opt, i) => (typeof opt === "string" ? opt : opt.value) + i,
|
||||||
),
|
),
|
||||||
() => (list().length ? null : $html("li", { class: "p-2 text-center opacity-50" }, tt("nodata")())),
|
() => (list().length ? null : Tag("li", { class: "p-2 text-center opacity-50" }, tt("nodata")())),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Badge.js
|
// components/Badge.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { ui } from "../core/utils.js";
|
import { ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +14,7 @@ import { ui } from "../core/utils.js";
|
|||||||
export const Badge = (props, children) => {
|
export const Badge = (props, children) => {
|
||||||
const { class: className, ...rest } = props;
|
const { class: className, ...rest } = props;
|
||||||
|
|
||||||
return $html("span", {
|
return Tag("span", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui('badge', className),
|
class: ui('badge', className),
|
||||||
}, children);
|
}, children);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Button.js
|
// components/Button.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { ui, val, getIcon } from "../core/utils.js";
|
import { ui, val, getIcon } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,12 +18,12 @@ export const Button = (props, children) => {
|
|||||||
|
|
||||||
const iconEl = getIcon(icon);
|
const iconEl = getIcon(icon);
|
||||||
|
|
||||||
return $html("button", {
|
return Tag("button", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui('btn', className),
|
class: ui('btn', className),
|
||||||
disabled: () => val(loading) || val(props.disabled),
|
disabled: () => val(loading) || val(props.disabled),
|
||||||
}, () => [
|
}, () => [
|
||||||
val(loading) && $html("span", { class: "loading loading-spinner" }),
|
val(loading) && Tag("span", { class: "loading loading-spinner" }),
|
||||||
iconEl,
|
iconEl,
|
||||||
children
|
children
|
||||||
].filter(Boolean));
|
].filter(Boolean));
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// components/Checkbox.js
|
// components/Checkbox.js
|
||||||
import { $html } from "../sigpro.js";
|
|
||||||
import { val, ui } from "../core/utils.js";
|
import { val, ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,19 +13,17 @@ import { val, ui } from "../core/utils.js";
|
|||||||
* - label, label-text, cursor-pointer
|
* - label, label-text, cursor-pointer
|
||||||
*/
|
*/
|
||||||
export const Checkbox = (props) => {
|
export const Checkbox = (props) => {
|
||||||
const { class: className, value, tooltip, toggle, label, ...rest } = props;
|
const { class: className, value, toggle, label, ...rest } = props;
|
||||||
|
|
||||||
const checkEl = $html("input", {
|
const checkEl = Tag("input", {
|
||||||
...rest,
|
...rest,
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
class: () => ui(val(toggle) ? "toggle" : "checkbox", className),
|
class: () => ui(val(toggle) ? "toggle" : "checkbox", className),
|
||||||
checked: value
|
checked: value
|
||||||
});
|
});
|
||||||
|
|
||||||
const layout = $html("label", { class: "label cursor-pointer justify-start gap-3" }, [
|
return Tag("label", { class: "label cursor-pointer justify-start gap-3" }, [
|
||||||
checkEl,
|
checkEl,
|
||||||
label ? $html("span", { class: "label-text" }, label) : null,
|
label ? Tag("span", { class: "label-text" }, label) : null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return tooltip ? $html("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
|
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Colorpicker.js
|
// components/Colorpicker.js
|
||||||
import { $, $html, $if } from "../sigpro.js";
|
// import { $, Tag, If } from "../sigpro.js";
|
||||||
import { val, ui } from "../core/utils.js";
|
import { val, ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,8 +30,8 @@ export const Colorpicker = (props) => {
|
|||||||
|
|
||||||
const getColor = () => val(value) || "#000000";
|
const getColor = () => val(value) || "#000000";
|
||||||
|
|
||||||
return $html("div", { class: ui('relative w-fit', className) }, [
|
return Tag("div", { class: ui('relative w-fit', className) }, [
|
||||||
$html(
|
Tag(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
@@ -43,27 +43,27 @@ export const Colorpicker = (props) => {
|
|||||||
...rest,
|
...rest,
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
$html("div", {
|
Tag("div", {
|
||||||
class: "size-5 rounded-sm shadow-inner border border-black/10 shrink-0",
|
class: "size-5 rounded-sm shadow-inner border border-black/10 shrink-0",
|
||||||
style: () => `background-color: ${getColor()}`,
|
style: () => `background-color: ${getColor()}`,
|
||||||
}),
|
}),
|
||||||
label ? $html("span", { class: "opacity-80" }, label) : null,
|
label ? Tag("span", { class: "opacity-80" }, label) : null,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
$if(isOpen, () =>
|
If(isOpen, () =>
|
||||||
$html(
|
Tag(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
class: "absolute left-0 mt-2 p-3 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[110] w-64 select-none",
|
class: "absolute left-0 mt-2 p-3 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[110] w-64 select-none",
|
||||||
onclick: (e) => e.stopPropagation(),
|
onclick: (e) => e.stopPropagation(),
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
$html(
|
Tag(
|
||||||
"div",
|
"div",
|
||||||
{ class: "grid grid-cols-8 gap-1" },
|
{ class: "grid grid-cols-8 gap-1" },
|
||||||
palette.map((c) =>
|
palette.map((c) =>
|
||||||
$html("button", {
|
Tag("button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
style: `background-color: ${c}`,
|
style: `background-color: ${c}`,
|
||||||
class: () => {
|
class: () => {
|
||||||
@@ -82,8 +82,8 @@ export const Colorpicker = (props) => {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
$if(isOpen, () =>
|
If(isOpen, () =>
|
||||||
$html("div", {
|
Tag("div", {
|
||||||
class: "fixed inset-0 z-[100]",
|
class: "fixed inset-0 z-[100]",
|
||||||
onclick: () => isOpen(false),
|
onclick: () => isOpen(false),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Datepicker.js
|
// components/Datepicker.js
|
||||||
import { $, $html, $if } from "../sigpro.js";
|
// import { $, Tag, If } from "../sigpro.js";
|
||||||
import { val, ui, getIcon } from "../core/utils.js";
|
import { val, ui, getIcon } from "../core/utils.js";
|
||||||
import { Input } from "./Input.js";
|
import { Input } from "./Input.js";
|
||||||
|
|
||||||
@@ -99,9 +99,9 @@ export const Datepicker = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const HourSlider = ({ value: hVal, onChange }) => {
|
const HourSlider = ({ value: hVal, onChange }) => {
|
||||||
return $html("div", { class: "flex-1" }, [
|
return Tag("div", { class: "flex-1" }, [
|
||||||
$html("div", { class: "flex gap-2 items-center" }, [
|
Tag("div", { class: "flex gap-2 items-center" }, [
|
||||||
$html("input", {
|
Tag("input", {
|
||||||
type: "range",
|
type: "range",
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 23,
|
max: 23,
|
||||||
@@ -112,14 +112,14 @@ export const Datepicker = (props) => {
|
|||||||
onChange(newHour);
|
onChange(newHour);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
$html("span", { class: "text-sm font-mono min-w-[48px] text-center" },
|
Tag("span", { class: "text-sm font-mono min-w-[48px] text-center" },
|
||||||
() => String(val(hVal)).padStart(2, "0") + ":00"
|
() => String(val(hVal)).padStart(2, "0") + ":00"
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return $html("div", { class: ui('relative w-full', className) }, [
|
return Tag("div", { class: ui('relative w-full', className) }, [
|
||||||
Input({
|
Input({
|
||||||
label,
|
label,
|
||||||
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
placeholder: placeholder || (isRangeMode() ? "Seleccionar rango..." : "Seleccionar fecha..."),
|
||||||
@@ -133,38 +133,38 @@ export const Datepicker = (props) => {
|
|||||||
...rest,
|
...rest,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
$if(isOpen, () =>
|
If(isOpen, () =>
|
||||||
$html(
|
Tag(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
class: "absolute left-0 mt-2 p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[100] w-80 select-none",
|
class: "absolute left-0 mt-2 p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[100] w-80 select-none",
|
||||||
onclick: (e) => e.stopPropagation(),
|
onclick: (e) => e.stopPropagation(),
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
$html("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
Tag("div", { class: "flex justify-between items-center mb-4 gap-1" }, [
|
||||||
$html("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) },
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(-1) },
|
||||||
getIcon("icon-[lucide--chevrons-left]")
|
getIcon("icon-[lucide--chevrons-left]")
|
||||||
),
|
),
|
||||||
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) },
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(-1) },
|
||||||
getIcon("icon-[lucide--chevron-left]")
|
getIcon("icon-[lucide--chevron-left]")
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
$html("span", { class: "font-bold uppercase flex-1 text-center" }, [
|
Tag("span", { class: "font-bold uppercase flex-1 text-center" }, [
|
||||||
() => internalDate().toLocaleString("es-ES", { month: "short", year: "numeric" }),
|
() => internalDate().toLocaleString("es-ES", { month: "short", year: "numeric" }),
|
||||||
]),
|
]),
|
||||||
$html("div", { class: "flex gap-0.5" }, [
|
Tag("div", { class: "flex gap-0.5" }, [
|
||||||
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) },
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => move(1) },
|
||||||
getIcon("icon-[lucide--chevron-right]")
|
getIcon("icon-[lucide--chevron-right]")
|
||||||
),
|
),
|
||||||
$html("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) },
|
Tag("button", { type: "button", class: "btn btn-ghost btn-xs px-1", onclick: () => moveYear(1) },
|
||||||
getIcon("icon-[lucide--chevrons-right]")
|
getIcon("icon-[lucide--chevrons-right]")
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
$html("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
Tag("div", { class: "grid grid-cols-7 gap-1", onmouseleave: () => hoverDate(null) }, [
|
||||||
...["L", "M", "X", "J", "V", "S", "D"].map((d) => $html("div", { class: "text-[10px] opacity-40 font-bold text-center" }, d)),
|
...["L", "M", "X", "J", "V", "S", "D"].map((d) => Tag("div", { class: "text-[10px] opacity-40 font-bold text-center" }, d)),
|
||||||
() => {
|
() => {
|
||||||
const d = internalDate();
|
const d = internalDate();
|
||||||
const year = d.getFullYear();
|
const year = d.getFullYear();
|
||||||
@@ -174,14 +174,14 @@ export const Datepicker = (props) => {
|
|||||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||||
|
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
for (let i = 0; i < offset; i++) nodes.push($html("div"));
|
for (let i = 0; i < offset; i++) nodes.push(Tag("div"));
|
||||||
|
|
||||||
for (let i = 1; i <= daysInMonth; i++) {
|
for (let i = 1; i <= daysInMonth; i++) {
|
||||||
const date = new Date(year, month, i);
|
const date = new Date(year, month, i);
|
||||||
const dStr = formatDate(date);
|
const dStr = formatDate(date);
|
||||||
|
|
||||||
nodes.push(
|
nodes.push(
|
||||||
$html(
|
Tag(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
@@ -218,9 +218,9 @@ export const Datepicker = (props) => {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
|
|
||||||
hour ? $html("div", { class: "mt-3 pt-2 border-t border-base-300" }, [
|
hour ? Tag("div", { class: "mt-3 pt-2 border-t border-base-300" }, [
|
||||||
isRangeMode()
|
isRangeMode()
|
||||||
? $html("div", { class: "flex gap-4" }, [
|
? Tag("div", { class: "flex gap-4" }, [
|
||||||
HourSlider({
|
HourSlider({
|
||||||
value: startHour,
|
value: startHour,
|
||||||
onChange: (newHour) => {
|
onChange: (newHour) => {
|
||||||
@@ -253,6 +253,6 @@ export const Datepicker = (props) => {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
$if(isOpen, () => $html("div", { class: "fixed inset-0 z-[90]", onclick: () => isOpen(false) })),
|
If(isOpen, () => Tag("div", { class: "fixed inset-0 z-[90]", onclick: () => isOpen(false) })),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Drawer.js
|
// components/Drawer.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { ui } from "../core/utils.js";
|
import { ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,11 +14,11 @@ export const Drawer = (props, children) => {
|
|||||||
|
|
||||||
const drawerId = id || `drawer-${Math.random().toString(36).slice(2, 9)}`;
|
const drawerId = id || `drawer-${Math.random().toString(36).slice(2, 9)}`;
|
||||||
|
|
||||||
return $html("div", {
|
return Tag("div", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui('drawer', className),
|
class: ui('drawer', className),
|
||||||
}, [
|
}, [
|
||||||
$html("input", {
|
Tag("input", {
|
||||||
id: drawerId,
|
id: drawerId,
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
class: "drawer-toggle",
|
class: "drawer-toggle",
|
||||||
@@ -27,18 +27,18 @@ export const Drawer = (props, children) => {
|
|||||||
if (typeof open === "function") open(e.target.checked);
|
if (typeof open === "function") open(e.target.checked);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$html("div", { class: "drawer-content" }, [
|
Tag("div", { class: "drawer-content" }, [
|
||||||
typeof content === "function" ? content() : content
|
typeof content === "function" ? content() : content
|
||||||
]),
|
]),
|
||||||
$html("div", { class: "drawer-side" }, [
|
Tag("div", { class: "drawer-side" }, [
|
||||||
$html("label", {
|
Tag("label", {
|
||||||
for: drawerId,
|
for: drawerId,
|
||||||
class: "drawer-overlay",
|
class: "drawer-overlay",
|
||||||
onclick: () => {
|
onclick: () => {
|
||||||
if (typeof open === "function") open(false);
|
if (typeof open === "function") open(false);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
$html("div", { class: "min-h-full bg-base-200 w-80" }, [
|
Tag("div", { class: "min-h-full bg-base-200 w-80" }, [
|
||||||
typeof side === "function" ? side() : side
|
typeof side === "function" ? side() : side
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Dropdown.js
|
// components/Dropdown.js
|
||||||
// import { $html, $for, $watch } from "../sigpro.js";
|
// import { Tag, For, Watch } from "../sigpro.js";
|
||||||
import { ui } from "../core/utils.js";
|
import { ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,11 +28,11 @@ if (typeof window !== 'undefined' && !window.__dropdownHandlerRegistered) {
|
|||||||
export const Dropdown = (props) => {
|
export const Dropdown = (props) => {
|
||||||
const { class: className, label, icon, items, ...rest } = props;
|
const { class: className, label, icon, items, ...rest } = props;
|
||||||
|
|
||||||
return $html("details", {
|
return Tag("details", {
|
||||||
...rest,
|
...rest,
|
||||||
class: ui('dropdown', className),
|
class: ui('dropdown', className),
|
||||||
}, [
|
}, [
|
||||||
$html("summary", {
|
Tag("summary", {
|
||||||
class: "btn m-1 flex items-center gap-2 list-none cursor-pointer",
|
class: "btn m-1 flex items-center gap-2 list-none cursor-pointer",
|
||||||
style: "display: inline-flex;",
|
style: "display: inline-flex;",
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
@@ -48,15 +48,15 @@ export const Dropdown = (props) => {
|
|||||||
() => icon ? (typeof icon === "function" ? icon() : icon) : null,
|
() => icon ? (typeof icon === "function" ? icon() : icon) : null,
|
||||||
() => label ? (typeof label === "function" ? label() : label) : null
|
() => label ? (typeof label === "function" ? label() : label) : null
|
||||||
]),
|
]),
|
||||||
$html("ul", {
|
Tag("ul", {
|
||||||
tabindex: "-1",
|
tabindex: "-1",
|
||||||
class: "dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box w-52 border border-base-300"
|
class: "dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box w-52 border border-base-300"
|
||||||
}, [
|
}, [
|
||||||
() => {
|
() => {
|
||||||
const currentItems = typeof items === "function" ? items() : (items || []);
|
const currentItems = typeof items === "function" ? items() : (items || []);
|
||||||
return currentItems.map(item =>
|
return currentItems.map(item =>
|
||||||
$html("li", {}, [
|
Tag("li", {}, [
|
||||||
$html("a", {
|
Tag("a", {
|
||||||
class: item.class || "",
|
class: item.class || "",
|
||||||
onclick: (e) => {
|
onclick: (e) => {
|
||||||
if (item.onclick) item.onclick(e);
|
if (item.onclick) item.onclick(e);
|
||||||
@@ -67,8 +67,8 @@ export const Dropdown = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
item.icon ? $html("span", {}, item.icon) : null,
|
item.icon ? Tag("span", {}, item.icon) : null,
|
||||||
$html("span", {}, item.label)
|
Tag("span", {}, item.label)
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Fab.js
|
// components/Fab.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { val, ui, getIcon } from "../core/utils.js";
|
import { val, ui, getIcon } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,14 +16,14 @@ import { val, ui, getIcon } from "../core/utils.js";
|
|||||||
export const Fab = (props) => {
|
export const Fab = (props) => {
|
||||||
const { class: className, icon, label, actions = [], position = "bottom-6 right-6", ...rest } = props;
|
const { class: className, icon, label, actions = [], position = "bottom-6 right-6", ...rest } = props;
|
||||||
|
|
||||||
return $html(
|
return Tag(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
...rest,
|
...rest,
|
||||||
class: ui(`fab absolute ${position} flex flex-col-reverse items-end gap-3 z-[100]`, className),
|
class: ui(`fab absolute ${position} flex flex-col-reverse items-end gap-3 z-[100]`, className),
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
$html(
|
Tag(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
tabindex: 0,
|
tabindex: 0,
|
||||||
@@ -37,9 +37,9 @@ export const Fab = (props) => {
|
|||||||
),
|
),
|
||||||
|
|
||||||
...val(actions).map((act) =>
|
...val(actions).map((act) =>
|
||||||
$html("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
Tag("div", { class: "flex items-center gap-3 transition-all duration-300" }, [
|
||||||
act.label ? $html("span", { class: "badge badge-ghost shadow-sm whitespace-nowrap" }, act.label) : null,
|
act.label ? Tag("span", { class: "badge badge-ghost shadow-sm whitespace-nowrap" }, act.label) : null,
|
||||||
$html(
|
Tag(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Fieldset.js
|
// components/Fieldset.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { val, ui } from "../core/utils.js";
|
import { val, ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,7 +13,7 @@ import { val, ui } from "../core/utils.js";
|
|||||||
export const Fieldset = (props, children) => {
|
export const Fieldset = (props, children) => {
|
||||||
const { class: className, legend, ...rest } = props;
|
const { class: className, legend, ...rest } = props;
|
||||||
|
|
||||||
return $html(
|
return Tag(
|
||||||
"fieldset",
|
"fieldset",
|
||||||
{
|
{
|
||||||
...rest,
|
...rest,
|
||||||
@@ -22,7 +22,7 @@ export const Fieldset = (props, children) => {
|
|||||||
[
|
[
|
||||||
() => {
|
() => {
|
||||||
const legendText = val(legend);
|
const legendText = val(legend);
|
||||||
return legendText ? $html("legend", { class: "fieldset-legend font-bold" }, [legendText]) : null;
|
return legendText ? Tag("legend", { class: "fieldset-legend font-bold" }, [legendText]) : null;
|
||||||
},
|
},
|
||||||
children,
|
children,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Fileinput.js
|
// components/Fileinput.js
|
||||||
import { $, $html, $if, $for } from "../sigpro.js";
|
// import { $, Tag, If, For } from "../sigpro.js";
|
||||||
import { ui, getIcon } from "../core/utils.js";
|
import { ui, getIcon } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +20,7 @@ import { ui, getIcon } from "../core/utils.js";
|
|||||||
* - btn, btn-ghost, btn-xs, btn-circle
|
* - btn, btn-ghost, btn-xs, btn-circle
|
||||||
*/
|
*/
|
||||||
export const Fileinput = (props) => {
|
export const Fileinput = (props) => {
|
||||||
const { class: className, tooltip, max = 2, accept = "*", onSelect, ...rest } = props;
|
const { class: className, tooltip, max = 2, accept = "*", onselect, ...rest } = props;
|
||||||
|
|
||||||
const selectedFiles = $([]);
|
const selectedFiles = $([]);
|
||||||
const isDragging = $(false);
|
const isDragging = $(false);
|
||||||
@@ -38,24 +38,24 @@ export const Fileinput = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectedFiles([...selectedFiles(), ...fileList]);
|
selectedFiles([...selectedFiles(), ...fileList]);
|
||||||
onSelect?.(selectedFiles());
|
onselect?.(selectedFiles());
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFile = (index) => {
|
const removeFile = (index) => {
|
||||||
const updated = selectedFiles().filter((_, i) => i !== index);
|
const updated = selectedFiles().filter((_, i) => i !== index);
|
||||||
selectedFiles(updated);
|
selectedFiles(updated);
|
||||||
onSelect?.(updated);
|
onselect?.(updated);
|
||||||
};
|
};
|
||||||
|
|
||||||
return $html("fieldset", { ...rest, class: ui('fieldset w-full p-0', className) }, [
|
return Tag("fieldset", { ...rest, class: ui('fieldset w-full p-0', className) }, [
|
||||||
$html(
|
Tag(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
class: () => `w-full ${tooltip ? "tooltip tooltip-top before:z-50 after:z-50" : ""}`,
|
class: () => `w-full ${tooltip ? "tooltip tooltip-top before:z-50 after:z-50" : ""}`,
|
||||||
"data-tip": tooltip,
|
"data-tip": tooltip,
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
$html(
|
Tag(
|
||||||
"label",
|
"label",
|
||||||
{
|
{
|
||||||
class: () => `
|
class: () => `
|
||||||
@@ -76,12 +76,12 @@ export const Fileinput = (props) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
$html("div", { class: "flex items-center gap-3 w-full" }, [
|
Tag("div", { class: "flex items-center gap-3 w-full" }, [
|
||||||
getIcon("icon-[lucide--upload]"),
|
getIcon("icon-[lucide--upload]"),
|
||||||
$html("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
Tag("span", { class: "text-sm opacity-70 truncate grow text-left" }, "Arrastra o selecciona archivos..."),
|
||||||
$html("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`),
|
Tag("span", { class: "text-[10px] opacity-40 shrink-0" }, `Máx ${max}MB`),
|
||||||
]),
|
]),
|
||||||
$html("input", {
|
Tag("input", {
|
||||||
type: "file",
|
type: "file",
|
||||||
multiple: true,
|
multiple: true,
|
||||||
accept: accept,
|
accept: accept,
|
||||||
@@ -93,22 +93,22 @@ export const Fileinput = (props) => {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
() => (error() ? $html("span", { class: "text-[10px] text-error mt-1 px-1 font-medium" }, error()) : null),
|
() => (error() ? Tag("span", { class: "text-[10px] text-error mt-1 px-1 font-medium" }, error()) : null),
|
||||||
|
|
||||||
$if(
|
If(
|
||||||
() => selectedFiles().length > 0,
|
() => selectedFiles().length > 0,
|
||||||
() =>
|
() =>
|
||||||
$html("ul", { class: "mt-2 space-y-1" }, [
|
Tag("ul", { class: "mt-2 space-y-1" }, [
|
||||||
$for(
|
For(
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
(file, index) =>
|
(file, index) =>
|
||||||
$html("li", { class: "flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300" }, [
|
Tag("li", { class: "flex items-center justify-between p-1.5 pl-3 text-xs bg-base-200/50 rounded-md border border-base-300" }, [
|
||||||
$html("div", { class: "flex items-center gap-2 truncate" }, [
|
Tag("div", { class: "flex items-center gap-2 truncate" }, [
|
||||||
$html("span", { class: "opacity-50" }, "📄"),
|
Tag("span", { class: "opacity-50" }, "📄"),
|
||||||
$html("span", { class: "truncate font-medium max-w-[200px]" }, file.name),
|
Tag("span", { class: "truncate font-medium max-w-[200px]" }, file.name),
|
||||||
$html("span", { class: "text-[9px] opacity-40" }, `(${(file.size / 1024).toFixed(0)} KB)`),
|
Tag("span", { class: "text-[9px] opacity-40" }, `(${(file.size / 1024).toFixed(0)} KB)`),
|
||||||
]),
|
]),
|
||||||
$html(
|
Tag(
|
||||||
"button",
|
"button",
|
||||||
{
|
{
|
||||||
type: "button",
|
type: "button",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Indicator.js
|
// components/Indicator.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { ui } from "../core/utils.js";
|
import { ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,12 +15,12 @@ import { ui } from "../core/utils.js";
|
|||||||
export const Indicator = (props, children) => {
|
export const Indicator = (props, children) => {
|
||||||
const { value, class: className, ...rest } = props;
|
const { value, class: className, ...rest } = props;
|
||||||
|
|
||||||
return $html("div", {
|
return Tag("div", {
|
||||||
...rest,
|
...rest,
|
||||||
class: "indicator"
|
class: "indicator"
|
||||||
}, () => [
|
}, () => [
|
||||||
// El indicador debe ir PRIMERO (antes que el children)
|
// El indicador debe ir PRIMERO (antes que el children)
|
||||||
value ? $html("span", {
|
value ? Tag("span", {
|
||||||
class: ui('indicator-item badge', className)
|
class: ui('indicator-item badge', className)
|
||||||
}, () => typeof value === "function" ? value() : value) : null,
|
}, () => typeof value === "function" ? value() : value) : null,
|
||||||
children
|
children
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
// components/Input.js
|
// components/Input.js
|
||||||
import { $, $html, $watch } from "../sigpro.js";
|
|
||||||
import { val, ui, getIcon } from "../core/utils.js";
|
import { val, ui, getIcon } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input component - Solo el input con ícono integrado a la izquierda
|
* Input component - Input con ícono integrado, toggle password, validación y floating label opcional
|
||||||
*
|
*
|
||||||
* daisyUI classes used:
|
* daisyUI classes used:
|
||||||
* - input, input-bordered, input-ghost, input-primary, input-secondary
|
* - input, input-bordered, input-ghost, input-primary, input-secondary
|
||||||
* - input-accent, input-info, input-success, input-warning, input-error
|
* - input-accent, input-info, input-success, input-warning, input-error
|
||||||
* - input-xs, input-sm, input-md, input-lg
|
* - input-xs, input-sm, input-md, input-lg
|
||||||
* - btn, btn-ghost, btn-xs, btn-sm, btn-md, btn-circle, opacity-50, hover:opacity-100
|
* - floating-label
|
||||||
|
* - btn, btn-ghost, btn-xs, btn-sm, btn-md, btn-circle
|
||||||
*/
|
*/
|
||||||
export const Input = (props) => {
|
export const Input = (props) => {
|
||||||
const {
|
const {
|
||||||
@@ -20,8 +20,8 @@ export const Input = (props) => {
|
|||||||
oninput,
|
oninput,
|
||||||
placeholder,
|
placeholder,
|
||||||
disabled,
|
disabled,
|
||||||
size,
|
|
||||||
validate,
|
validate,
|
||||||
|
label,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -71,10 +71,10 @@ export const Input = (props) => {
|
|||||||
return classes.trim();
|
return classes.trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
const inputElement = $html("input", {
|
const inputElement = Tag("input", {
|
||||||
...rest,
|
...rest,
|
||||||
type: () => (isPassword ? (visible() ? "text" : "password") : type),
|
type: () => (isPassword ? (visible() ? "text" : "password") : type),
|
||||||
placeholder: placeholder || " ",
|
placeholder: placeholder || (label ? " " : placeholder),
|
||||||
class: inputClasses,
|
class: inputClasses,
|
||||||
value: value,
|
value: value,
|
||||||
oninput: handleInput,
|
oninput: handleInput,
|
||||||
@@ -82,29 +82,32 @@ export const Input = (props) => {
|
|||||||
"aria-invalid": () => hasError() ? "true" : "false",
|
"aria-invalid": () => hasError() ? "true" : "false",
|
||||||
});
|
});
|
||||||
|
|
||||||
return $html(
|
const inputContent = () => [
|
||||||
"div",
|
inputElement,
|
||||||
{ class: "relative w-full" },
|
leftIcon ? Tag("div", {
|
||||||
() => [
|
class: "absolute left-3 inset-y-0 flex items-center pointer-events-none text-base-content/60",
|
||||||
inputElement,
|
}, leftIcon) : null,
|
||||||
leftIcon ? $html("div", {
|
isPassword ? Tag("button", {
|
||||||
class: "absolute left-3 inset-y-0 flex items-center pointer-events-none text-base-content/60",
|
type: "button",
|
||||||
}, leftIcon) : null,
|
class: ui("absolute right-3 inset-y-0 flex items-center", "btn btn-ghost btn-circle opacity-50 hover:opacity-100", buttonSize()),
|
||||||
isPassword ? $html("button", {
|
onclick: (e) => {
|
||||||
type: "button",
|
e.preventDefault();
|
||||||
class: ui(
|
visible(!visible());
|
||||||
"absolute right-3 inset-y-0 flex items-center",
|
}
|
||||||
"btn btn-ghost btn-circle opacity-50 hover:opacity-100",
|
}, () => getPasswordIcon()) : null,
|
||||||
buttonSize()
|
Tag("div", {
|
||||||
),
|
class: "text-error text-xs mt-1 px-3 absolute -bottom-5 left-0",
|
||||||
onclick: (e) => {
|
}, () => hasError() ? errorMsg() : null),
|
||||||
e.preventDefault();
|
];
|
||||||
visible(!visible());
|
|
||||||
}
|
// Con floating label - añadir w-full para que ocupe todo el ancho
|
||||||
}, () => getPasswordIcon()) : null,
|
if (label) {
|
||||||
$html("div", {
|
return Tag("label", { class: ui("floating-label w-full", className) }, () => [
|
||||||
class: "text-error text-xs mt-1 px-3 absolute -bottom-5 left-0",
|
Tag("div", { class: "relative w-full" }, inputContent),
|
||||||
}, () => hasError() ? errorMsg() : null),
|
Tag("span", {}, val(label))
|
||||||
]
|
]);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
// Sin label
|
||||||
|
return Tag("div", { class: "relative w-full" }, inputContent);
|
||||||
};
|
};
|
||||||
@@ -1,28 +1,18 @@
|
|||||||
// components/Label.js
|
// components/Label.js
|
||||||
import { $, $html } from "../sigpro.js";
|
|
||||||
import { ui, val } from "../core/utils.js";
|
import { ui, val } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
|
||||||
* Label component
|
|
||||||
*
|
|
||||||
* daisyUI classes used:
|
|
||||||
* - label
|
|
||||||
* - floating-label
|
|
||||||
*/
|
|
||||||
export const Label = (props) => {
|
export const Label = (props) => {
|
||||||
const { children, value, floating = false, error, required, class: className, ...rest } = props;
|
const { children, value, floating = false, class: className, ...rest } = props;
|
||||||
|
|
||||||
if (floating) {
|
if (floating) {
|
||||||
return $html("label", { class: ui("floating-label w-full", className), ...rest }, () => [
|
return Tag("label", { class: ui("floating-label", className), ...rest }, () => [
|
||||||
value ? $html("span", {}, value) : null,
|
typeof children === 'function' ? children() : children,
|
||||||
children,
|
value ? Tag("span", {}, val(value)) : null
|
||||||
error ? $html("span", { class: "text-error text-xs" }, val(error)) : null,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $html("label", { class: ui("input w-full", className), ...rest }, () => [
|
return Tag("label", { class: ui("label", className), ...rest }, () => [
|
||||||
value ? $html("span", { class: "label" }, value) : null,
|
value ? Tag("span", { class: "label-text" }, val(value)) : null,
|
||||||
children,
|
typeof children === 'function' ? children() : children
|
||||||
error ? $html("span", { class: "text-error text-xs" }, val(error)) : null,
|
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/List.js
|
// components/List.js
|
||||||
import { $html, $if, $for } from "../sigpro.js";
|
// import { Tag, If, For } from "../sigpro.js";
|
||||||
import { ui, val } from "../core/utils.js";
|
import { ui, val } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,16 +12,22 @@ import { ui, val } from "../core/utils.js";
|
|||||||
* - flex, items-center, gap-2
|
* - flex, items-center, gap-2
|
||||||
*/
|
*/
|
||||||
export const List = (props) => {
|
export const List = (props) => {
|
||||||
const { class: className, items, header, render, keyFn = (item, index) => item.id ?? index, ...rest } = props;
|
const { class: className, items, header, render = (item) => item, keyFn = (item, index) => item.id ?? index, ...rest } = props;
|
||||||
|
|
||||||
const listItems = $for(
|
const listItems = For(
|
||||||
items,
|
items,
|
||||||
(item, index) => $html("li", { class: "list-row" }, [render(item, index)]),
|
(item, index) => Tag("li", {
|
||||||
|
class: "list-row",
|
||||||
|
style: "width: 100%; display: block;"
|
||||||
|
}, [
|
||||||
|
Tag("div", { style: "width: 100%;" }, [render(item, index)])
|
||||||
|
]),
|
||||||
keyFn
|
keyFn
|
||||||
);
|
);
|
||||||
|
|
||||||
return $html("ul", {
|
return Tag("ul", {
|
||||||
...rest,
|
...rest,
|
||||||
|
style: "display: block; width: 100%;",
|
||||||
class: ui('list bg-base-100 rounded-box shadow-md', className),
|
class: ui('list bg-base-100 rounded-box shadow-md', className),
|
||||||
}, header ? [$if(header, () => $html("li", { class: "p-4 pb-2 text-xs opacity-60" }, [val(header)])), listItems] : listItems);
|
}, header ? [If(header, () => Tag("li", { class: "p-4 pb-2 text-xs opacity-60", style: "width: 100%;" }, [val(header)])), listItems] : listItems);
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Menu.js
|
// components/Menu.js
|
||||||
import { $html, $for } from "../sigpro.js";
|
// import { Tag, For } from "../sigpro.js";
|
||||||
import { val, ui } from "../core/utils.js";
|
import { val, ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,22 +15,22 @@ export const Menu = (props) => {
|
|||||||
const { class: className, items, ...rest } = props;
|
const { class: className, items, ...rest } = props;
|
||||||
|
|
||||||
const renderItems = (items) =>
|
const renderItems = (items) =>
|
||||||
$for(
|
For(
|
||||||
() => items || [],
|
() => items || [],
|
||||||
(it) =>
|
(it) =>
|
||||||
$html("li", {}, [
|
Tag("li", {}, [
|
||||||
it.children
|
it.children
|
||||||
? $html("details", { open: it.open }, [
|
? Tag("details", { open: it.open }, [
|
||||||
$html("summary", {}, [it.icon && $html("span", { class: "mr-2" }, it.icon), it.label]),
|
Tag("summary", {}, [it.icon && Tag("span", { class: "mr-2" }, it.icon), it.label]),
|
||||||
$html("ul", {}, renderItems(it.children)),
|
Tag("ul", {}, renderItems(it.children)),
|
||||||
])
|
])
|
||||||
: $html("a", { class: () => (val(it.active) ? "active" : ""), onclick: it.onclick }, [
|
: Tag("a", { class: () => (val(it.active) ? "active" : ""), onclick: it.onclick }, [
|
||||||
it.icon && $html("span", { class: "mr-2" }, it.icon),
|
it.icon && Tag("span", { class: "mr-2" }, it.icon),
|
||||||
it.label,
|
it.label,
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
(it, i) => it.label || i,
|
(it, i) => it.label || i,
|
||||||
);
|
);
|
||||||
|
|
||||||
return $html("ul", { ...rest, class: ui('menu bg-base-200 rounded-box', className) }, renderItems(items));
|
return Tag("ul", { ...rest, class: ui('menu bg-base-200 rounded-box', className) }, renderItems(items));
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Modal.js
|
// components/Modal.js
|
||||||
import { $html, $watch } from "../sigpro.js";
|
// import { Tag, Watch } from "../sigpro.js";
|
||||||
import { ui } from "../core/utils.js";
|
import { ui } from "../core/utils.js";
|
||||||
import { tt } from "../core/i18n.js";
|
import { tt } from "../core/i18n.js";
|
||||||
import { Button } from "./Button.js";
|
import { Button } from "./Button.js";
|
||||||
@@ -28,13 +28,13 @@ export const Modal = (props, children) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$watch(() => handleOpen());
|
Watch(() => handleOpen());
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
if (typeof open === "function") open(false);
|
if (typeof open === "function") open(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return $html("dialog", {
|
return Tag("dialog", {
|
||||||
...rest,
|
...rest,
|
||||||
ref: (el) => {
|
ref: (el) => {
|
||||||
dialogElement = el;
|
dialogElement = el;
|
||||||
@@ -44,22 +44,22 @@ export const Modal = (props, children) => {
|
|||||||
onclose: close,
|
onclose: close,
|
||||||
oncancel: close
|
oncancel: close
|
||||||
}, [
|
}, [
|
||||||
$html("div", { class: "modal-box" }, [
|
Tag("div", { class: "modal-box" }, [
|
||||||
title ? $html("h3", { class: "text-lg font-bold mb-4" }, () =>
|
title ? Tag("h3", { class: "text-lg font-bold mb-4" }, () =>
|
||||||
typeof title === "function" ? title() : title
|
typeof title === "function" ? title() : title
|
||||||
) : null,
|
) : null,
|
||||||
$html("div", { class: "py-2" }, [
|
Tag("div", { class: "py-2" }, [
|
||||||
typeof children === "function" ? children() : children
|
typeof children === "function" ? children() : children
|
||||||
]),
|
]),
|
||||||
$html("div", { class: "modal-action" }, [
|
Tag("div", { class: "modal-action" }, [
|
||||||
$html("form", { method: "dialog", class: "flex gap-2" }, [
|
Tag("form", { method: "dialog", class: "flex gap-2" }, [
|
||||||
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
...(Array.isArray(buttons) ? buttons : [buttons]).filter(Boolean),
|
||||||
Button({ type: "submit" }, tt("close")())
|
Button({ type: "submit" }, tt("close")())
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
$html("form", { method: "dialog", class: "modal-backdrop" }, [
|
Tag("form", { method: "dialog", class: "modal-backdrop" }, [
|
||||||
$html("button", {}, "close")
|
Tag("button", {}, "close")
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Navbar.js
|
// components/Navbar.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { ui } from "../core/utils.js";
|
import { ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,5 +12,5 @@ import { ui } from "../core/utils.js";
|
|||||||
export const Navbar = (props, children) => {
|
export const Navbar = (props, children) => {
|
||||||
const { class: className, ...rest } = props;
|
const { class: className, ...rest } = props;
|
||||||
|
|
||||||
return $html("div", { ...rest, class: ui('navbar bg-base-100 shadow-sm px-4', className) }, children);
|
return Tag("div", { ...rest, class: ui('navbar bg-base-100 shadow-sm px-4', className) }, children);
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Radio.js
|
// components/Radio.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { val, ui } from "../core/utils.js";
|
import { val, ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +15,7 @@ import { val, ui } from "../core/utils.js";
|
|||||||
export const Radio = (props) => {
|
export const Radio = (props) => {
|
||||||
const { class: className, label, tooltip, value, inputValue, name, ...rest } = props;
|
const { class: className, label, tooltip, value, inputValue, name, ...rest } = props;
|
||||||
|
|
||||||
const radioEl = $html("input", {
|
const radioEl = Tag("input", {
|
||||||
...rest,
|
...rest,
|
||||||
type: "radio",
|
type: "radio",
|
||||||
name: name,
|
name: name,
|
||||||
@@ -28,10 +28,10 @@ export const Radio = (props) => {
|
|||||||
|
|
||||||
if (!label && !tooltip) return radioEl;
|
if (!label && !tooltip) return radioEl;
|
||||||
|
|
||||||
const layout = $html("label", { class: "label cursor-pointer justify-start gap-3" }, [
|
const layout = Tag("label", { class: "label cursor-pointer justify-start gap-3" }, [
|
||||||
radioEl,
|
radioEl,
|
||||||
label ? $html("span", { class: "label-text" }, label) : null,
|
label ? Tag("span", { class: "label-text" }, label) : null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return tooltip ? $html("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
|
return tooltip ? Tag("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Range.js
|
// components/Range.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { val, ui } from "../core/utils.js";
|
import { val, ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +15,7 @@ import { val, ui } from "../core/utils.js";
|
|||||||
export const Range = (props) => {
|
export const Range = (props) => {
|
||||||
const {class: className, label, tooltip, value, ...rest } = props;
|
const {class: className, label, tooltip, value, ...rest } = props;
|
||||||
|
|
||||||
const rangeEl = $html("input", {
|
const rangeEl = Tag("input", {
|
||||||
...rest,
|
...rest,
|
||||||
type: "range",
|
type: "range",
|
||||||
class: ui('range', className),
|
class: ui('range', className),
|
||||||
@@ -25,10 +25,10 @@ export const Range = (props) => {
|
|||||||
|
|
||||||
if (!label && !tooltip) return rangeEl;
|
if (!label && !tooltip) return rangeEl;
|
||||||
|
|
||||||
const layout = $html("div", { class: "flex flex-col gap-2" }, [
|
const layout = Tag("div", { class: "flex flex-col gap-2" }, [
|
||||||
label ? $html("span", { class: "label-text" }, label) : null,
|
label ? Tag("span", { class: "label-text" }, label) : null,
|
||||||
rangeEl
|
rangeEl
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return tooltip ? $html("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
|
return tooltip ? Tag("div", { class: "tooltip", "data-tip": tooltip }, layout) : layout;
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// components/Rating.js
|
// components/Rating.js
|
||||||
import { $html } from "../sigpro.js";
|
// import { Tag } from "../sigpro.js";
|
||||||
import { val, ui } from "../core/utils.js";
|
import { val, ui } from "../core/utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +15,7 @@ export const Rating = (props) => {
|
|||||||
|
|
||||||
const ratingGroup = `rating-${Math.random().toString(36).slice(2, 7)}`;
|
const ratingGroup = `rating-${Math.random().toString(36).slice(2, 7)}`;
|
||||||
|
|
||||||
return $html(
|
return Tag(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
...rest,
|
...rest,
|
||||||
@@ -23,7 +23,7 @@ export const Rating = (props) => {
|
|||||||
},
|
},
|
||||||
Array.from({ length: val(count) }, (_, i) => {
|
Array.from({ length: val(count) }, (_, i) => {
|
||||||
const starValue = i + 1;
|
const starValue = i + 1;
|
||||||
return $html("input", {
|
return Tag("input", {
|
||||||
type: "radio",
|
type: "radio",
|
||||||
name: ratingGroup,
|
name: ratingGroup,
|
||||||
class: `mask ${mask}`,
|
class: `mask ${mask}`,
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user