story-az-card-kit
Version:
A flexible and customizable story card component with multiple layouts
173 lines (146 loc) • 4.93 kB
JavaScript
/**
* Creates a story card element for vanilla JavaScript usage
* @param {Object} options - Story card configuration
* @param {string} options.title - Card title
* @param {string} options.description - Card description
* @param {string} options.image - Image URL
* @param {string} options.author - Author name
* @param {string} options.date - Publication date
* @param {string[]} options.tags - Array of tags
* @param {string} options.layout - Layout type (vertical, horizontal, etc.)
* @param {string} options.theme - Theme type (default, dark, minimal, etc.)
* @param {Function} options.onClick - Click handler function
* @param {string} options.className - Additional CSS classes
* @returns {HTMLElement} - The created story card element
*/
export const createStoryCard = (options = {}) => {
const {
title,
description,
image,
author,
date,
tags = [],
layout = 'vertical',
theme = 'default',
onClick,
className = ''
} = options;
const card = document.createElement('div');
const baseClass = 'story-card';
const layoutClass = `story-card--${layout}`;
const themeClass = `story-card--${theme}`;
const cardClasses = [
baseClass,
layoutClass,
themeClass,
className
].filter(Boolean).join(' ');
card.className = cardClasses;
card.setAttribute('role', 'button');
card.setAttribute('tabindex', '0');
// Create image section
if (image) {
const imageContainer = document.createElement('div');
imageContainer.className = 'story-card__image';
const img = document.createElement('img');
img.src = image;
img.alt = title || '';
img.loading = 'lazy';
imageContainer.appendChild(img);
card.appendChild(imageContainer);
}
// Create content section
const content = document.createElement('div');
content.className = 'story-card__content';
// Add title
if (title) {
const titleElement = document.createElement('h3');
titleElement.className = 'story-card__title';
titleElement.textContent = title;
content.appendChild(titleElement);
}
// Add description
if (description) {
const descriptionElement = document.createElement('p');
descriptionElement.className = 'story-card__description';
descriptionElement.textContent = description;
content.appendChild(descriptionElement);
}
// Add meta information
if (author || date) {
const meta = document.createElement('div');
meta.className = 'story-card__meta';
if (author) {
const authorElement = document.createElement('span');
authorElement.className = 'story-card__author';
authorElement.textContent = author;
meta.appendChild(authorElement);
}
if (date) {
const dateElement = document.createElement('time');
dateElement.className = 'story-card__date';
dateElement.setAttribute('datetime', date);
dateElement.textContent = new Date(date).toLocaleDateString();
meta.appendChild(dateElement);
}
content.appendChild(meta);
}
// Add tags
if (tags.length > 0) {
const tagsContainer = document.createElement('div');
tagsContainer.className = 'story-card__tags';
tags.forEach(tag => {
const tagElement = document.createElement('span');
tagElement.className = 'story-card__tag';
tagElement.textContent = tag;
tagsContainer.appendChild(tagElement);
});
content.appendChild(tagsContainer);
}
card.appendChild(content);
// Add event listeners
if (onClick) {
card.addEventListener('click', (e) => {
onClick(e, { title, description, image, author, date, tags });
});
card.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onClick(e, { title, description, image, author, date, tags });
}
});
}
return card;
};
/**
* Creates multiple story cards and returns them as an array
* @param {Array} stories - Array of story configurations
* @returns {HTMLElement[]} - Array of created story card elements
*/
export const createStoryCards = (stories = []) => {
return stories.map(story => createStoryCard(story));
};
/**
* Creates a grid container with story cards
* @param {Array} stories - Array of story configurations
* @param {Object} options - Grid options
* @returns {HTMLElement} - Grid container with story cards
*/
export const createStoryCardGrid = (stories = [], options = {}) => {
const {
columns = 3,
gap = 'medium',
className = ''
} = options;
const grid = document.createElement('div');
const gridClass = `story-card-grid story-card-grid--${columns}-cols story-card-grid--gap-${gap}`;
const gridClasses = [
gridClass,
className
].filter(Boolean).join(' ');
grid.className = gridClasses;
const cards = createStoryCards(stories);
cards.forEach(card => grid.appendChild(card));
return grid;
};