501 lines
15 KiB
Markdown
501 lines
15 KiB
Markdown
# 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>
|