520 lines
19 KiB
Markdown
520 lines
19 KiB
Markdown
# 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>
|